Commit 6d225728 by zhengnw@sobot.com

album 1.0.0

parent 0ad71fca
......@@ -32,9 +32,10 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'com.google.android.material:material:1.4.+'
implementation project(':sobot_common')
implementation project(':sobot_widget')
implementation project(':sobot_album')
// implementation 'com.sobot.library:widget:0.3'
// api 'com.sobot.library:sobotcommon:1.0'
// api 'com.sobot.library:sobotcommon_x:1.0'
......
......@@ -9,10 +9,14 @@ import android.net.Uri;
import android.widget.ImageView;
import android.widget.TextView;
import com.sobot.common.login.SobotLoginTools;
import com.sobot.common.login.callback.SobotResultBlock;
import com.sobot.common.login.callback.SobotResultCode;
import com.sobot.common.login.model.HostModel;
import androidx.annotation.NonNull;
import com.sobot.album.Action;
import com.sobot.album.AlbumConfig;
import com.sobot.album.AlbumFile;
import com.sobot.album.SobotAlbum;
import com.sobot.album.SobotMediaLoader;
import com.sobot.album.api.widget.Widget;
import com.sobot.common.utils.SobotImageUtils;
import com.sobot.pictureframe.SobotBitmapUtil;
import com.sobot.utils.SobotLogUtils;
......@@ -25,12 +29,14 @@ import com.sobot.widget.refresh.layout.footer.ClassicsFooter;
import com.sobot.widget.refresh.layout.header.ClassicsHeader;
import com.sobot.widget.ui.SobotMarkConfig;
import com.sobot.widget.ui.base.SobotBaseActivity;
import com.sobot.widget.ui.permission.SobotPermissionListenerImpl;
import com.sobot.widget.ui.rich.HtmlToolUtils;
import com.sobot.widget.ui.toast.SobotToastUtil;
import com.sobot.widget.ui.webview.SobotWebViewActivity;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class MainActivity extends SobotBaseActivity {
......@@ -255,8 +261,61 @@ public class MainActivity extends SobotBaseActivity {
//// 是否隐藏底部工具栏 默认false显示, true 隐藏
// intent.putExtra("isHideToolBar", true);
// startActivity(intent);
permissionListener = new SobotPermissionListenerImpl() {
@Override
public void onPermissionSuccessListener() {
openAlbum();
}
};
if (checkIsShowPermissionPop(getString(R.string.sobot_memory_card), getString(R.string.sobot_memory_card_yongtu), 1, 4)) {
return;
}
if (!checkStoragePermission(4)) {
return;
}
openAlbum();
}
private void openAlbum() {
SobotAlbum.initialize(AlbumConfig.newBuilder(this)
.setAlbumLoader(new SobotMediaLoader())
.setLocale(Locale.getDefault())
.build());
SobotAlbum.album(MainActivity.this)
.multipleChoice()
.columnCount(4)
.selectCount(4)
.camera(false)
.cameraVideoQuality(1)
.cameraVideoLimitDuration(Integer.MAX_VALUE)
.cameraVideoLimitBytes(Integer.MAX_VALUE)
.checkedList(mAlbumFiles)
.widget(
Widget.newDarkBuilder(MainActivity.this)
.title("")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
if (result.size() > 0) {
}
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
//取消选择
}
})
.start();
}
private ArrayList<AlbumFile> mAlbumFiles = new ArrayList<>();//编辑的图片地址
@Override
protected void initData() {
}
......
......@@ -4,6 +4,7 @@ include ':sobot_utils'
include ':sobot_gson'
include ':sobot_common'
include ':sobot_widget'
include ':sobot_album'
include ':app'
rootProject.name = "Sobot_module_Dev"
/build
\ No newline at end of file
apply plugin: 'com.android.library'
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 17
targetSdkVersion 34
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
buildTypes {
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
consumerProguardFiles 'proguard-rules.pro'
}
qa_test {
}
qa_api {
}
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
compileOnly 'com.google.android.material:material:1.4.+'
compileOnly 'androidx.appcompat:appcompat:1.0.0'
compileOnly 'androidx.recyclerview:recyclerview:1.0.0'
api 'com.sobot.library:widget_x:1.2.1'
api 'com.sobot.library:picture_x:1.1.8'
}
//添加发布到mavenCentral脚本
apply from: './sobot-album_publish-mavencentral.gradle'
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
apply plugin: 'maven-publish'
apply plugin: 'signing'
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.source
exclude "**/R.class"
exclude "**/BuildConfig.class"
}
ext {
PUBLISH_GROUP_ID = "com.sobot.library" //项目包名
PUBLISH_ARTIFACT_ID = 'album' //项目名
PUBLISH_VERSION = '1.0.0' //版本号
}
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.secretKeyRingFile"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
println "Found secret props file, loading props"
Properties p = new Properties()
p.load(new FileInputStream(secretPropsFile))
p.each { name, value ->
ext[name] = value
}
} else {
println "No props file, loading env vars"
}
publishing {
publications {
release(MavenPublication) {
// The coordinates of the library, being set from variables that
// we'll set up in a moment
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
// Two artifacts, the `aar` and the sources
artifact("$buildDir/outputs/aar/${project.getName()}-release.aar")
artifact androidSourcesJar
// Self-explanatory metadata for the most part
pom {
name = PUBLISH_ARTIFACT_ID
description = 'sobotcommon'
// If your project has a dedicated site, use its URL here
url = 'http://code.zhichidata.com/sobot_android/Sobot_module_Dev'
licenses {
license {
//协议类型,一般默认Apache License2.0的话不用改:
name = 'The Apache License, Version 2.0'
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id = 'app_dev@sobot.com'
name = 'zhichi'
email = 'app_dev@sobot.com'
}
}
// Version control info, if you're using GitHub, follow the format as seen here
scm {
//修改成你的Git地址:
connection = 'git@code.zhichidata.com:sobot_android/Sobot_module_Dev.git'
developerConnection = 'git@code.zhichidata.com:sobot_android/Sobot_module_Dev.git'
//分支地址:
url = 'http://code.zhichidata.com/sobot_android/Sobot_module_Dev/master'
}
// A slightly hacky fix so that your POM will include any transitive dependencies
// that your library builds upon
// withXml {
// def dependenciesNode = asNode().appendNode('dependencies')
//
// project.configurations.implementation.allDependencies.each {
// def dependencyNode = dependenciesNode.appendNode('dependency')
// dependencyNode.appendNode('groupId', it.group)
// dependencyNode.appendNode('artifactId', it.name)
// dependencyNode.appendNode('version', it.version)
// }
// }
withXml {
def dependenciesNode = asNode().appendNode('dependencies')
//跳过“unspecified”的依赖项
//避免出现问题:
//Execution failed for task ':sample:checkDebugAarMetadata'.
//> Could not resolve all files for configuration ':sample:debugRuntimeClasspath'.
// > Could not find :unspecified:.
// Required by:
project.configurations.implementation.allDependencies.each {
if (it.name != 'unspecified') {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
}
repositories {
// The repository to publish to, Sonatype/MavenCentral
maven {
// This is an arbitrary name, you may also use "mavencentral" or
// any other name that's descriptive for you
//项目名称
name = "sobotwidget"
def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
// You only need this if you want to publish snapshots, otherwise just set the URL
// to the release repo directly
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
// The username and password we've fetched earlier
credentials {
username ossrhUsername
password ossrhPassword
}
}
}
}
signing {
sign publishing.publications
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sobot.album">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- To handle the reselection within the app on Android 14 (API level 34) -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<application>
<provider
android:name=".provider.CameraFileProvider"
android:authorities="${applicationId}.app.file.provider"
android:exported="false"
android:grantUriPermissions="true"
android:multiprocess="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/album_camera_provider" />
</provider>
<activity
android:name="com.sobot.album.activity.CameraActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.activity.SobotAlbumActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.activity.DefineStyleActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.activity.GalleryActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait" />
<activity
android:name="com.sobot.album.activity.AlbumFilterActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.app.album.AlbumActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.app.album.GalleryActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.app.album.NullActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
<activity
android:name="com.sobot.album.app.gallery.GalleryAlbumActivity"
android:configChanges="orientation|keyboardHidden|screenSize|touchscreen|navigation|locale|fontScale|uiMode|screenLayout|smallestScreenSize"
android:screenOrientation="portrait"
android:theme="@style/sobot_activity_def_theme" />
</application>
</manifest>
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album;
import androidx.annotation.NonNull;
public interface Action<T> {
/**
* When the action responds.
*
* @param result the result of the action.
*/
void onAction(@NonNull T result);
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album;
import android.content.Context;
import java.util.Locale;
/**
* <p>Album config.</p>
* Created by Yan Zhenjie on 2017/3/31.
*/
public class AlbumConfig {
/**
* Create a new builder.
*/
public static Builder newBuilder(Context context) {
return new Builder(context);
}
private AlbumLoader mLoader;
private Locale mLocale;
private AlbumConfig(Builder builder) {
this.mLoader = builder.mLoader == null ? AlbumLoader.DEFAULT : builder.mLoader;
this.mLocale = builder.mLocale == null ? Locale.getDefault() : builder.mLocale;
}
/**
* Get {@link AlbumLoader}.
*
* @return {@link AlbumLoader}.
*/
public AlbumLoader getAlbumLoader() {
return mLoader;
}
/**
* Get {@link Locale}.
*
* @return {@link Locale}.
*/
public Locale getLocale() {
return mLocale;
}
public static final class Builder {
private AlbumLoader mLoader;
private Locale mLocale;
private Builder(Context context) {
}
/**
* Set album loader.
*
* @param loader {@link AlbumLoader}.
* @return {@link Builder}.
*/
public Builder setAlbumLoader(AlbumLoader loader) {
this.mLoader = loader;
return this;
}
/**
* Set locale for language.
*
* @param locale {@link Locale}.
* @return {@link Builder}.
*/
public Builder setLocale(Locale locale) {
this.mLocale = locale;
return this;
}
/**
* Create AlbumConfig.
*
* @return {@link AlbumConfig}.
*/
public AlbumConfig build() {
return new AlbumConfig(this);
}
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntDef;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class AlbumFile implements Parcelable, Comparable<AlbumFile> {
public static final int TYPE_IMAGE = 1;
public static final int TYPE_VIDEO = 2;
public static final int TYPE_ADD = 3;
@Retention(RetentionPolicy.SOURCE)
@IntDef({TYPE_IMAGE, TYPE_VIDEO,TYPE_ADD})
public @interface MediaType {
}
/**
* 本地文件路径.
*/
private String mPath;
/**
* 本地的uri
*/
private Uri uri;
private String fileNumKey;
/**
* 远程的地址
*/
private String fileUrl;
/**
* 文件名字.
*/
private String fileName;
/**
* 文件夹名字.
*/
private String mBucketName;
/**
* 文件扩展类型.
*/
private String mMimeType;
/**
* 添加日期.
*/
private long mAddDate;
/**
* Latitude
*/
private float mLatitude;
/**
* Longitude.
*/
private float mLongitude;
/**
* 大小.
*/
private long mSize;
/**
* Duration.
*/
private long mDuration;
/**
* Thumb path.
*/
private String mThumbPath;
/**
* MediaType.
*/
private int mMediaType;
/**
* Checked.
*/
private boolean isChecked;
/**
* Enabled.
*/
private boolean isDisable;
public AlbumFile() {
}
public Uri getUri() {
return uri;
}
public void setUri(Uri uri) {
this.uri = uri;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
@Override
public int compareTo(AlbumFile o) {
long time = o.getAddDate() - getAddDate();
if (time > Integer.MAX_VALUE)
return Integer.MAX_VALUE;
else if (time < -Integer.MAX_VALUE)
return -Integer.MAX_VALUE;
return (int) time;
}
@Override
public boolean equals(Object obj) {
if (obj != null && obj instanceof AlbumFile) {
AlbumFile o = (AlbumFile) obj;
String inPath = o.getPath();
if (mPath != null && inPath != null) {
return mPath.equals(inPath);
}
}
return super.equals(obj);
}
@Override
public int hashCode() {
return mPath != null ? mPath.hashCode() : super.hashCode();
}
public String getPath() {
return mPath;
}
public void setPath(String path) {
mPath = path;
}
public String getBucketName() {
return mBucketName;
}
public void setBucketName(String bucketName) {
mBucketName = bucketName;
}
public String getMimeType() {
return mMimeType;
}
public void setMimeType(String mimeType) {
mMimeType = mimeType;
}
public long getAddDate() {
return mAddDate;
}
public void setAddDate(long addDate) {
mAddDate = addDate;
}
public float getLatitude() {
return mLatitude;
}
public void setLatitude(float latitude) {
mLatitude = latitude;
}
public float getLongitude() {
return mLongitude;
}
public void setLongitude(float longitude) {
mLongitude = longitude;
}
public long getSize() {
return mSize;
}
public void setSize(long size) {
mSize = size;
}
public long getDuration() {
return mDuration;
}
public void setDuration(long duration) {
mDuration = duration;
}
public String getThumbPath() {
return mThumbPath;
}
public void setThumbPath(String thumbPath) {
mThumbPath = thumbPath;
}
@MediaType
public int getMediaType() {
return mMediaType;
}
public void setMediaType(@MediaType int mediaType) {
mMediaType = mediaType;
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
public boolean isDisable() {
return isDisable;
}
public void setDisable(boolean disable) {
this.isDisable = disable;
}
public String getFileNumKey() {
return fileNumKey;
}
public void setFileNumKey(String fileNumKey) {
this.fileNumKey = fileNumKey;
}
public String getFileUrl() {
return fileUrl;
}
public void setFileUrl(String fileUrl) {
this.fileUrl = fileUrl;
}
protected AlbumFile(Parcel in) {
mPath = in.readString();
mBucketName = in.readString();
mMimeType = in.readString();
mAddDate = in.readLong();
mLatitude = in.readFloat();
mLongitude = in.readFloat();
mSize = in.readLong();
mDuration = in.readLong();
mThumbPath = in.readString();
mMediaType = in.readInt();
isChecked = in.readByte() != 0;
isDisable = in.readByte() != 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPath);
dest.writeString(mBucketName);
dest.writeString(mMimeType);
dest.writeLong(mAddDate);
dest.writeFloat(mLatitude);
dest.writeFloat(mLongitude);
dest.writeLong(mSize);
dest.writeLong(mDuration);
dest.writeString(mThumbPath);
dest.writeInt(mMediaType);
dest.writeByte((byte) (isChecked ? 1 : 0));
dest.writeByte((byte) (isDisable ? 1 : 0));
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<AlbumFile> CREATOR = new Creator<AlbumFile>() {
@Override
public AlbumFile createFromParcel(Parcel in) {
return new AlbumFile(in);
}
@Override
public AlbumFile[] newArray(int size) {
return new AlbumFile[size];
}
};
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
/**
* <p>Album folder, contains selected status and pictures.</p>
* Created by Yan Zhenjie on 2016/10/14.
*/
public class AlbumFolder implements Parcelable {
/**
* Folder name.
*/
private String name;
/**
* Image list in folder.
*/
private ArrayList<AlbumFile> mAlbumFiles = new ArrayList<>();
/**
* checked.
*/
private boolean isChecked;
public AlbumFolder() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ArrayList<AlbumFile> getAlbumFiles() {
return mAlbumFiles;
}
public void addAlbumFile(AlbumFile albumFile) {
mAlbumFiles.add(albumFile);
}
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
protected AlbumFolder(Parcel in) {
name = in.readString();
mAlbumFiles = in.createTypedArrayList(AlbumFile.CREATOR);
isChecked = in.readByte() != 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeTypedList(mAlbumFiles);
dest.writeByte((byte) (isChecked ? 1 : 0));
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<AlbumFolder> CREATOR = new Creator<AlbumFolder>() {
@Override
public AlbumFolder createFromParcel(Parcel in) {
return new AlbumFolder(in);
}
@Override
public AlbumFolder[] newArray(int size) {
return new AlbumFolder[size];
}
};
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album;
import android.widget.ImageView;
public interface AlbumLoader {
AlbumLoader DEFAULT = new AlbumLoader() {
@Override
public void load(ImageView imageView, AlbumFile albumFile) {
}
@Override
public void load(ImageView imageView, String url) {
}
};
/**
* Load a preview of the album file.
*
* @param imageView {@link ImageView}.
* @param albumFile the media object may be a picture or video.
*/
void load(ImageView imageView, AlbumFile albumFile);
/**
* Load thumbnails of pictures or videos, either local file or remote file.
*
* @param imageView {@link ImageView}.
* @param url The url of the file, local path or remote path.
*/
void load(ImageView imageView, String url);
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album;
public interface Filter<T> {
/**
* Filter the file.
*
* @param attributes attributes of file.
* @return filter returns true, otherwise false.
*/
boolean filter(T attributes);
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album;
import android.content.Context;
public interface ItemAction<T> {
/**
* When the action responds.
*
* @param context context.
* @param item item.
*/
void onAction(Context context, T item);
}
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.util.Log;
import androidx.annotation.IntDef;
import com.sobot.album.api.AlbumMultipleWrapper;
import com.sobot.album.api.AlbumSingleWrapper;
import com.sobot.album.api.BasicGalleryWrapper;
import com.sobot.album.api.GalleryAlbumWrapper;
import com.sobot.album.api.GalleryWrapper;
import com.sobot.album.api.ImageCameraWrapper;
import com.sobot.album.api.ImageMultipleWrapper;
import com.sobot.album.api.ImageSingleWrapper;
import com.sobot.album.api.VideoCameraWrapper;
import com.sobot.album.api.VideoMultipleWrapper;
import com.sobot.album.api.VideoSingleWrapper;
import com.sobot.album.api.camera.AlbumCamera;
import com.sobot.album.api.camera.Camera;
import com.sobot.album.api.choice.AlbumChoice;
import com.sobot.album.api.choice.Choice;
import com.sobot.album.api.choice.ImageChoice;
import com.sobot.album.api.choice.VideoChoice;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* 图片选择器
*/
public final class SobotAlbum {
// All.
public static final String KEY_INPUT_WIDGET = "KEY_INPUT_WIDGET";
public static final String KEY_INPUT_CHECKED_LIST = "KEY_INPUT_CHECKED_LIST";
// Album.
public static final String KEY_INPUT_FUNCTION = "KEY_INPUT_FUNCTION";
public static final int FUNCTION_CHOICE_IMAGE = 0;
public static final int FUNCTION_CHOICE_VIDEO = 1;
public static final int FUNCTION_CHOICE_ALBUM = 2;
public static final int FUNCTION_CAMERA_IMAGE = 0;
public static final int FUNCTION_CAMERA_VIDEO = 1;
public static final String KEY_INPUT_CHOICE_MODE = "KEY_INPUT_CHOICE_MODE";
public static final int MODE_MULTIPLE = 1;
public static final int MODE_SINGLE = 2;
public static final String KEY_INPUT_COLUMN_COUNT = "KEY_INPUT_COLUMN_COUNT";
public static final String KEY_INPUT_ALLOW_CAMERA = "KEY_INPUT_ALLOW_CAMERA";
public static final String KEY_INPUT_LIMIT_COUNT = "KEY_INPUT_LIMIT_COUNT";
// Gallery.
public static final String KEY_INPUT_CURRENT_POSITION = "KEY_INPUT_CURRENT_POSITION";
public static final String KEY_INPUT_GALLERY_CHECKABLE = "KEY_INPUT_GALLERY_CHECKABLE";
// Camera.
public static final String KEY_INPUT_FILE_PATH = "KEY_INPUT_FILE_PATH";
public static final String KEY_INPUT_CAMERA_QUALITY = "KEY_INPUT_CAMERA_QUALITY";
public static final String KEY_INPUT_CAMERA_DURATION = "KEY_INPUT_CAMERA_DURATION";
public static final String KEY_INPUT_CAMERA_BYTES = "KEY_INPUT_CAMERA_BYTES";
// Filter.
public static final String KEY_INPUT_FILTER_VISIBILITY = "KEY_INPUT_FILTER_VISIBILITY";
@IntDef({FUNCTION_CHOICE_IMAGE, FUNCTION_CHOICE_VIDEO, FUNCTION_CHOICE_ALBUM})
@Retention(RetentionPolicy.SOURCE)
public @interface ChoiceFunction {
}
@IntDef({FUNCTION_CAMERA_IMAGE, FUNCTION_CAMERA_VIDEO})
@Retention(RetentionPolicy.SOURCE)
public @interface CameraFunction {
}
@IntDef({MODE_MULTIPLE, MODE_SINGLE})
@Retention(RetentionPolicy.SOURCE)
public @interface ChoiceMode {
}
private static AlbumConfig sAlbumConfig;
/**
* Initialize Album.
*
* @param albumConfig {@link AlbumConfig}.
*/
public static void initialize(AlbumConfig albumConfig) {
if (sAlbumConfig == null) sAlbumConfig = albumConfig;
else
Log.w("Album", new IllegalStateException("Illegal operation, only allowed to configure once."));
}
/**
* Get the album configuration.
*/
public static AlbumConfig getAlbumConfig() {
if (sAlbumConfig == null) {
sAlbumConfig = AlbumConfig.newBuilder(null).build();
}
return sAlbumConfig;
}
/**
* Open the camera from the activity.
*/
public static Camera<ImageCameraWrapper, VideoCameraWrapper> camera(Context context) {
return new AlbumCamera(context);
}
/**
* Select images.
*/
public static Choice<ImageMultipleWrapper, ImageSingleWrapper> image(Context context) {
return new ImageChoice(context);
}
/**
* Select videos.
*/
public static Choice<VideoMultipleWrapper, VideoSingleWrapper> video(Context context) {
return new VideoChoice(context);
}
/**
* Select images and videos.
*/
public static Choice<AlbumMultipleWrapper, AlbumSingleWrapper> album(Context context) {
return new AlbumChoice(context);
}
/**
* Preview picture.
*/
public static GalleryWrapper gallery(Context context) {
return new GalleryWrapper(context);
}
/**
* Preview Album.
*/
public static GalleryAlbumWrapper galleryAlbum(Context context) {
return new GalleryAlbumWrapper(context);
}
/**
* Open the camera from the activity.
*/
public static Camera<ImageCameraWrapper, VideoCameraWrapper> camera(Activity activity) {
return new AlbumCamera(activity);
}
/**
* Select images.
*/
public static Choice<ImageMultipleWrapper, ImageSingleWrapper> image(Activity activity) {
return new ImageChoice(activity);
}
/**
* Select videos.
*/
public static Choice<VideoMultipleWrapper, VideoSingleWrapper> video(Activity activity) {
return new VideoChoice(activity);
}
/**
* Select images and videos.
*/
public static Choice<AlbumMultipleWrapper, AlbumSingleWrapper> album(Activity activity) {
return new AlbumChoice(activity);
}
/**
* Preview picture.
*/
public static BasicGalleryWrapper<GalleryWrapper, String, String, String> gallery(Activity activity) {
return new GalleryWrapper(activity);
}
/**
* Preview Album.
*/
public static BasicGalleryWrapper<GalleryAlbumWrapper, AlbumFile, String, AlbumFile> galleryAlbum(Activity activity) {
return new GalleryAlbumWrapper(activity);
}
/**
* Open the camera from the activity.
*/
public static Camera<ImageCameraWrapper, VideoCameraWrapper> camera(Fragment fragment) {
return new AlbumCamera(fragment.getActivity());
}
/**
* Select images.
*/
public static Choice<ImageMultipleWrapper, ImageSingleWrapper> image(Fragment fragment) {
return new ImageChoice(fragment.getActivity());
}
/**
* Select videos.
*/
public static Choice<VideoMultipleWrapper, VideoSingleWrapper> video(Fragment fragment) {
return new VideoChoice(fragment.getActivity());
}
/**
* Select images and videos.
*/
public static Choice<AlbumMultipleWrapper, AlbumSingleWrapper> album(Fragment fragment) {
return new AlbumChoice(fragment.getActivity());
}
/**
* Preview picture.
*/
public static BasicGalleryWrapper<GalleryWrapper, String, String, String> gallery(Fragment fragment) {
return new GalleryWrapper(fragment.getActivity());
}
/**
* Preview Album.
*/
public static BasicGalleryWrapper<GalleryAlbumWrapper, AlbumFile, String, AlbumFile> galleryAlbum(Fragment fragment) {
return new GalleryAlbumWrapper(fragment.getActivity());
}
}
\ No newline at end of file
package com.sobot.album;
import android.net.Uri;
import android.widget.ImageView;
import com.sobot.pictureframe.SobotBitmapUtil;
import com.sobot.utils.SobotStringUtils;
public class SobotMediaLoader implements AlbumLoader {
@Override
public void load(ImageView imageView, AlbumFile albumFile) {
if (albumFile != null && SobotStringUtils.isNoEmpty(albumFile.getPath())) {
load(imageView, albumFile.getPath());
}
}
@Override
public void load(ImageView imageView, String url) {
if (SobotStringUtils.isNoEmpty(url) && imageView != null) {
SobotBitmapUtil.display(imageView.getContext(), url, imageView);
}
}
public void load(ImageView imageView, Uri uri) {
// if (uri != null && imageView != null) {
// imageView.setImageURI(uri);
// }
}
}
\ No newline at end of file
package com.sobot.album.activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.util.AlbumUtils;
import java.util.List;
public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private LayoutInflater mInflater;
private OnItemClickListener mItemClickListener;
private List<AlbumFile> mAlbumFiles;
private Context mContext;
public Adapter(Context context, OnItemClickListener itemClickListener) {
this.mInflater = LayoutInflater.from(context);
this.mItemClickListener = itemClickListener;
this.mContext = context;
}
public void notifyDataSetChanged(List<AlbumFile> imagePathList) {
this.mAlbumFiles = imagePathList;
super.notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
AlbumFile albumFile = mAlbumFiles.get(position);
if (albumFile.getMediaType() == AlbumFile.TYPE_IMAGE) {
return AlbumFile.TYPE_IMAGE;
} else {
return AlbumFile.TYPE_VIDEO;
}
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case AlbumFile.TYPE_IMAGE: {
return new ImageViewHolder(mContext, mInflater.inflate(R.layout.item_content_image, parent, false), mItemClickListener);
}
case AlbumFile.TYPE_VIDEO: {
return new VideoViewHolder(mContext, mInflater.inflate(R.layout.item_content_video, parent, false), mItemClickListener);
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case AlbumFile.TYPE_IMAGE: {
((ImageViewHolder) holder).setData(mAlbumFiles.get(position));
break;
}
case AlbumFile.TYPE_VIDEO: {
((VideoViewHolder) holder).setData(mAlbumFiles.get(position));
break;
}
}
}
@Override
public int getItemCount() {
return mAlbumFiles == null ? 0 : mAlbumFiles.size();
}
private static class ImageViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final OnItemClickListener mItemClickListener;
private ImageView mIvImage;
private Context mContext;
ImageViewHolder(Context context, View itemView, OnItemClickListener itemClickListener) {
super(itemView);
this.mContext = context;
this.mItemClickListener = itemClickListener;
this.mIvImage = itemView.findViewById(R.id.iv_album_content_image);
itemView.setOnClickListener(this);
}
public void setData(AlbumFile albumFile) {
SobotAlbum.getAlbumConfig().
getAlbumLoader().
load(mIvImage, albumFile);
}
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getAdapterPosition());
}
}
}
private static class VideoViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final OnItemClickListener mItemClickListener;
private ImageView mIvImage;
private TextView mTvDuration;
private Context mContext;
VideoViewHolder(Context context, View itemView, OnItemClickListener itemClickListener) {
super(itemView);
this.mContext = context;
this.mItemClickListener = itemClickListener;
this.mIvImage = itemView.findViewById(R.id.iv_album_content_image);
this.mTvDuration = itemView.findViewById(R.id.tv_duration);
itemView.setOnClickListener(this);
}
void setData(AlbumFile albumFile) {
SobotAlbum.getAlbumConfig().
getAlbumLoader().
load(mIvImage, albumFile);
mTvDuration.setText(AlbumUtils.convertDuration(albumFile.getDuration()));
}
@Override
public void onClick(View v) {
if (mItemClickListener != null) {
mItemClickListener.onItemClick(v, getAdapterPosition());
}
}
}
}
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.Action;
import com.sobot.album.AlbumFile;
import com.sobot.album.Filter;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.widget.divider.Api21ItemDivider;
import com.sobot.album.widget.divider.Divider;
import java.util.ArrayList;
public class AlbumFilterActivity extends AppCompatActivity {
// private Toolbar mToolbar;
private TextView mTvMessage;
private Adapter mAdapter;
private ArrayList<AlbumFile> mAlbumFiles;
private boolean mAfterFilterVisibility;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_album_filter);
// mToolbar = findViewById(R.id.toolbar);
// setSupportActionBar(mToolbar);
mTvMessage = findViewById(R.id.tv_message);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
Divider divider = new Api21ItemDivider(Color.TRANSPARENT, 10, 10);
recyclerView.addItemDecoration(divider);
mAdapter = new Adapter(this, new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
previewAlbum(position);
}
});
recyclerView.setAdapter(mAdapter);
}
/**
* Select picture, from album.
*/
private void selectAlbum() {
SobotAlbum.album(this)
.multipleChoice()
.filterMimeType(new Filter<String>() { // MimeType of File.
@Override
public boolean filter(String attributes) {
// MimeType: image/jpeg, image/png, video/mp4, video/3gp...
return attributes.contains("jpeg");
}
})
// .filterSize() // File size.
// .filterDuration() // Video duration.
.afterFilterVisibility(mAfterFilterVisibility)
.columnCount(2)
.selectCount(6)
.camera(true)
.checkedList(mAlbumFiles)
.widget(
Widget.newDarkBuilder(this)
.title("albun filter")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(AlbumFilterActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
/**
* Preview image, to album.
*/
private void previewAlbum(int position) {
if (mAlbumFiles == null || mAlbumFiles.size() == 0) {
Toast.makeText(this, R.string.no_selected, Toast.LENGTH_LONG).show();
} else {
SobotAlbum.galleryAlbum(this)
.checkable(true)
.checkedList(mAlbumFiles)
.currentPosition(position)
.widget(
Widget.newDarkBuilder(this)
.title("Album filter")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.start();
}
}
/* @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_album, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
} else if (id == R.id.menu_eye) {
previewAlbum(0);
} else if (id == R.id.menu_album) {
new AlertDialog.Builder(this)
.setCancelable(false)
.setTitle(R.string.app_name)
.setMessage(R.string.hint_filter_after_visibility)
.setNeutralButton(R.string.filter_after_visibility_gone, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mAfterFilterVisibility = false;
selectAlbum();
}
})
.setPositiveButton(R.string.filter_after_visibility_visible, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mAfterFilterVisibility = true;
selectAlbum();
}
})
.show();
}
return true;
}*/
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.activity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import com.sobot.album.Action;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
public class CameraActivity extends AppCompatActivity {
TextView mTextView;
private ImageView mImageView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
// Toolbar toolbar = findViewById(R.id.toolbar);
// setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
assert actionBar != null;
actionBar.setDisplayHomeAsUpEnabled(true);
mTextView = findViewById(R.id.tv_message);
mImageView = findViewById(R.id.image_view);
}
private void takePicture() {
SobotAlbum.camera(this)
.image()
// .filePath()
.onResult(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
mTextView.setText(result);
SobotAlbum.getAlbumConfig()
.getAlbumLoader()
.load(mImageView, result);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(CameraActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
private void recordVideo() {
SobotAlbum.camera(this)
.video()
// .filePath()
.quality(1)
.limitDuration(Integer.MAX_VALUE)
.limitBytes(Integer.MAX_VALUE)
.onResult(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
mTextView.setText(result);
SobotAlbum.getAlbumConfig()
.getAlbumLoader()
.load(mImageView, result);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(CameraActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
/* @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_album_camera, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
} else if (id == R.id.menu_image_capture) {
takePicture();
} else if (id == R.id.menu_video_capture) {
recordVideo();
}
return true;
}*/
}
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.Action;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.widget.divider.Api21ItemDivider;
import com.sobot.album.widget.divider.Divider;
import java.util.ArrayList;
public class DefineStyleActivity extends AppCompatActivity {
// private Toolbar mToolbar;
private TextView mTvMessage;
private Adapter mAdapter;
private ArrayList<AlbumFile> mAlbumFiles;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_album_ui);
// mToolbar = findViewById(R.id.toolbar);
// setSupportActionBar(mToolbar);
mTvMessage = findViewById(R.id.tv_message);
RecyclerView recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
Divider divider = new Api21ItemDivider(Color.TRANSPARENT, 10, 10);
recyclerView.addItemDecoration(divider);
mAdapter = new Adapter(this, new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
previewAlbum(position);
}
});
recyclerView.setAdapter(mAdapter);
}
/**
* Select picture, from album.
*/
private void selectAlbum() {
SobotAlbum.album(this)
.multipleChoice()
.selectCount(6)
.camera(true)
.checkedList(mAlbumFiles)
.widget(
Widget.newLightBuilder(this)
.title("define style")
.statusBarColor(Color.WHITE)
.toolBarColor(Color.WHITE)
.mediaItemCheckSelector(Color.BLUE, Color.GREEN)
.bucketItemCheckSelector(Color.RED, Color.YELLOW)
.buttonStyle(
Widget.ButtonStyle.newLightBuilder(this)
.setButtonSelector(Color.WHITE, Color.WHITE)
.build()
)
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(DefineStyleActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
/**
* Preview image, to album.
*/
private void previewAlbum(int position) {
if (mAlbumFiles == null || mAlbumFiles.size() == 0) {
Toast.makeText(this, R.string.no_selected, Toast.LENGTH_LONG).show();
} else {
SobotAlbum.galleryAlbum(this)
.checkable(true)
.checkedList(mAlbumFiles)
.currentPosition(position)
.widget(
Widget.newLightBuilder(this)
.toolBarColor(Color.WHITE)
.statusBarColor(Color.WHITE)
.mediaItemCheckSelector(Color.GREEN, Color.RED)
.bucketItemCheckSelector(Color.GREEN, Color.RED)
.buttonStyle(
Widget.ButtonStyle.newLightBuilder(this)
.setButtonSelector(Color.WHITE, Color.GRAY)
.build()
)
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.start();
}
}
/*@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_album, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
} else if (id == R.id.menu_eye) {
previewAlbum(0);
} else if (id == R.id.menu_album) {
selectAlbum();
}
return true;
}*/
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.activity;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.sobot.album.Action;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import java.util.ArrayList;
import java.util.Collections;
public class GalleryActivity extends AppCompatActivity {
private static final String[] IMAGE_PATH_LIST = {
"http://pic1.win4000.com/mobile/1/520d9410ebc79.jpg", "http://pic1.win4000.com/mobile/1/520d941218b04.jpg",
"http://wapfile.desktx.com/7681280/0925/160925tn4a5sz3at2.jpg", "http://wapfile.desktx.com/7681280/0925/160925ruuqfaqwuvx.jpg",
"http://wapfile.desktx.com/7681280/0925/160925a0x5knyt3ue.jpg", "http://wapfile.desktx.com/7681280/0925/160925jrfl5dnvln1.jpg",
"http://wapfile.desktx.com/7681280/0925/160925csx5pu2ysam.jpg", "http://wapfile.desktx.com/7681280/0925/160925m4lgeuakuc5.jpg",
"http://wapfile.desktx.com/7681280/0925/160925gcmbg5gycqw.jpg", "http://wapfile.desktx.com/7681280/0925/160925o0zf2pbozw1.jpg",
"http://wapfile.desktx.com/7681280/0925/160925danhsef1uzu.jpg", "http://wapfile.desktx.com/7681280/0925/160925y5lmcrqslm4.jpg",
"http://wapfile.desktx.com/7681280/0925/160925njt3dm2hlw0.jpg", "http://wapfile.desktx.com/7681280/0925/160925xgesspjxztt.jpg",
"http://wapfile.desktx.com/7681280/0925/160925a5dg2zsddtx.jpg", "http://wapfile.desktx.com/7681280/0925/1609252fupmrdasrp.jpg",
"http://wapfile.desktx.com/7681280/0925/160925lasaphesxkd.jpg", "http://wapfile.desktx.com/7681280/0925/160925ti2kged45bp.jpg",
"http://wapfile.desktx.com/7681280/0925/160925dsx3uyapoen.jpg", "http://wapfile.desktx.com/7681280/0925/160925largrb3p2l4.jpg",
"http://wapfile.desktx.com/7681280/0925/160925l0vaht45uy1.jpg", "http://wapfile.desktx.com/7681280/0925/160925054jrhaerro.jpg",
"http://wapfile.desktx.com/7681280/0925/1609255mlt2so5yp2.jpg", "http://wapfile.desktx.com/7681280/0925/160925bm3jmwdptie.jpg",
"http://wapfile.desktx.com/7681280/0925/1609251brfaryoizu.jpg", "http://wapfile.desktx.com/7681280/0925/1609254qfjkbwjlox.jpg",
"http://wapfile.desktx.com/7681280/0925/160925memb5c4pcnt.jpg", "http://wapfile.desktx.com/7681280/0925/160925c5i4dbhh2rh.jpg",
"http://wapfile.desktx.com/7681280/0925/1609254pmjqhppmah.jpg", "http://wapfile.desktx.com/7681280/0925/16092505gvkuoampz.jpg",
"http://wapfile.desktx.com/7681280/0925/1609254dxk3vpsrvj.jpg", "http://wapfile.desktx.com/7681280/0925/160925bgyxuik3j4y.jpg",
"http://wapfile.desktx.com/7681280/0925/160925jbhhtendosg.jpg", "http://wapfile.desktx.com/7681280/0925/160925knnv51uglia.jpg",
"http://wapfile.desktx.com/7681280/0925/160925vajxn2aqfkc.jpg", "http://wapfile.desktx.com/7681280/0925/160925xehnehy5frf.jpg",
"http://wapfile.desktx.com/7681280/0925/160925e2rajc4zi4k.jpg", "http://wapfile.desktx.com/7681280/0925/160925dfxg2wlarga.jpg",
"http://wapfile.desktx.com/7681280/0925/160925vuifhcqactt.jpg", "http://wapfile.desktx.com/7681280/0925/160925rcxiez3ikcg.jpg"
};
// private Toolbar mToolbar;
private CheckBox mCheckBox;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_gallery);
// mToolbar = findViewById(R.id.toolbar);
// setSupportActionBar(mToolbar);
// getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mCheckBox = findViewById(R.id.checkbox);
findViewById(R.id.btn_gallery).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
previewImages();
}
});
}
private void previewImages() {
ArrayList<String> imageList = new ArrayList<>();
Collections.addAll(imageList, IMAGE_PATH_LIST);
SobotAlbum.gallery(this)
.checkedList(imageList)
.checkable(mCheckBox.isChecked())
.widget(
Widget.newDarkBuilder(this)
.title("gallery ")
.build()
)
.onResult(new Action<ArrayList<String>>() {
@Override
public void onAction(@NonNull ArrayList<String> result) {
// TODO If it is optional, here you can accept the results of user selection.
}
})
.start();
}
/* @Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
finish();
}
return true;
}*/
@Override
protected void onDestroy() {
super.onDestroy();
}
}
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.Action;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.widget.divider.Api21ItemDivider;
import com.sobot.album.widget.divider.Divider;
import com.sobot.widget.ui.base.SobotBaseActivity;
import java.util.ArrayList;
/**
* 选择后的图片显示
*/
public class SobotAlbumActivity extends SobotBaseActivity {
private TextView mTvMessage;
private RecyclerView recyclerView;
private Adapter mAdapter;
private ArrayList<AlbumFile> mAlbumFiles;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected int getContentViewResId() {
return R.layout.sobot_activity_album;
}
@Override
protected void initView() throws InterruptedException {
mTvMessage = findViewById(R.id.tv_message);
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
Divider divider = new Api21ItemDivider(Color.TRANSPARENT, 10, 10);
recyclerView.addItemDecoration(divider);
//图片和视频
setTitle(R.string.sobot_album_select_images);
getLeftMenu().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
finish();
}
});
}
@Override
protected void initData() {
mAdapter = new Adapter(this, new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
previewAlbum(position);
}
});
recyclerView.setAdapter(mAdapter);
selectAlbum();
}
/**
* 选择图片
*/
private void selectImage() {
SobotAlbum.image(this)
.multipleChoice()
.camera(true)
.columnCount(2)
.selectCount(6)
.checkedList(mAlbumFiles)
.widget(
Widget.newDarkBuilder(this)
.title("称得上是粉色的")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(SobotAlbumActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
/**
* 从相册选择图片、视频
*/
private void selectAlbum() {
SobotAlbum.album(this)
.multipleChoice()
.columnCount(2)
.selectCount(15)
.camera(true)
.cameraVideoQuality(1)
.cameraVideoLimitDuration(Integer.MAX_VALUE)
.cameraVideoLimitBytes(Integer.MAX_VALUE)
.checkedList(mAlbumFiles)
.widget(
Widget.newDarkBuilder(this)
.title("测试")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(SobotAlbumActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
/**
* Preview image, to album.
*/
private void previewAlbum(int position) {
if (mAlbumFiles == null || mAlbumFiles.size() == 0) {
Toast.makeText(this, R.string.no_selected, Toast.LENGTH_LONG).show();
} else {
SobotAlbum.galleryAlbum(this)
.checkable(true)
.checkedList(mAlbumFiles)
.currentPosition(position)
.widget(
Widget.newDarkBuilder(this)
.title("测试")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.start();
}
}
/**
* Preview image, to album.
*/
private void previewImage(int position) {
if (mAlbumFiles == null || mAlbumFiles.size() == 0) {
Toast.makeText(this, R.string.no_selected, Toast.LENGTH_LONG).show();
} else {
SobotAlbum.galleryAlbum(this)
.checkable(true)
.checkedList(mAlbumFiles)
.currentPosition(position)
.widget(
Widget.newDarkBuilder(this)
.title("测试")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.start();
}
}
/**
* Select picture, from album.
*/
private void selectVideo() {
SobotAlbum.video(this)
.multipleChoice()
.columnCount(2)
.selectCount(6)
.camera(true)
.checkedList(mAlbumFiles)
.widget(
Widget.newDarkBuilder(this)
.title("")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.onCancel(new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Toast.makeText(SobotAlbumActivity.this, R.string.canceled, Toast.LENGTH_LONG).show();
}
})
.start();
}
/**
* Preview image, to album.
*/
private void previewVideo(int position) {
if (mAlbumFiles == null || mAlbumFiles.size() == 0) {
Toast.makeText(this, R.string.no_selected, Toast.LENGTH_LONG).show();
} else {
SobotAlbum.galleryAlbum(this)
.checkable(true)
.checkedList(mAlbumFiles)
.currentPosition(position)
.widget(
Widget.newDarkBuilder(this)
.title("")
.build()
)
.onResult(new Action<ArrayList<AlbumFile>>() {
@Override
public void onAction(@NonNull ArrayList<AlbumFile> result) {
mAlbumFiles = result;
mAdapter.notifyDataSetChanged(mAlbumFiles);
mTvMessage.setVisibility(result.size() > 0 ? View.VISIBLE : View.GONE);
}
})
.start();
}
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.IntRange;
import com.sobot.album.AlbumFile;
import com.sobot.album.Filter;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.album.AlbumActivity;
import java.util.ArrayList;
public class AlbumMultipleWrapper extends BasicChoiceAlbumWrapper<AlbumMultipleWrapper, ArrayList<AlbumFile>, String, ArrayList<AlbumFile>> {
private int mLimitCount = Integer.MAX_VALUE;
private Filter<Long> mDurationFilter;
public AlbumMultipleWrapper(Context context) {
super(context);
}
/**
* Set the list has been selected.
*
* @param checked the data list.
*/
public final AlbumMultipleWrapper checkedList(ArrayList<AlbumFile> checked) {
this.mChecked = checked;
return this;
}
/**
* Set the maximum number to be selected.
*
* @param count the maximum number.
*/
public AlbumMultipleWrapper selectCount(@IntRange(from = 1, to = Integer.MAX_VALUE) int count) {
this.mLimitCount = count;
return this;
}
/**
* Filter video duration.
*
* @param filter filter.
*/
public AlbumMultipleWrapper filterDuration(Filter<Long> filter) {
this.mDurationFilter = filter;
return this;
}
@Override
public void start() {
AlbumActivity.sSizeFilter = mSizeFilter;
AlbumActivity.sMimeFilter = mMimeTypeFilter;
AlbumActivity.sDurationFilter = mDurationFilter;
AlbumActivity.sResult = mResult;
AlbumActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, AlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putParcelableArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST, mChecked);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CHOICE_ALBUM);
intent.putExtra(SobotAlbum.KEY_INPUT_CHOICE_MODE, SobotAlbum.MODE_MULTIPLE);
intent.putExtra(SobotAlbum.KEY_INPUT_COLUMN_COUNT, mColumnCount);
intent.putExtra(SobotAlbum.KEY_INPUT_ALLOW_CAMERA, mHasCamera);
intent.putExtra(SobotAlbum.KEY_INPUT_LIMIT_COUNT, mLimitCount);
intent.putExtra(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY, mFilterVisibility);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_QUALITY, mQuality);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_DURATION, mLimitDuration);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_BYTES, mLimitBytes);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import com.sobot.album.AlbumFile;
import com.sobot.album.Filter;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.album.AlbumActivity;
import java.util.ArrayList;
public class AlbumSingleWrapper extends BasicChoiceAlbumWrapper<AlbumSingleWrapper, ArrayList<AlbumFile>, String, AlbumFile> {
private Filter<Long> mDurationFilter;
public AlbumSingleWrapper(Context context) {
super(context);
}
/**
* Filter video duration.
*
* @param filter filter.
*/
public AlbumSingleWrapper filterDuration(Filter<Long> filter) {
this.mDurationFilter = filter;
return this;
}
@Override
public void start() {
AlbumActivity.sSizeFilter = mSizeFilter;
AlbumActivity.sMimeFilter = mMimeTypeFilter;
AlbumActivity.sDurationFilter = mDurationFilter;
AlbumActivity.sResult = mResult;
AlbumActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, AlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CHOICE_ALBUM);
intent.putExtra(SobotAlbum.KEY_INPUT_CHOICE_MODE, SobotAlbum.MODE_SINGLE);
intent.putExtra(SobotAlbum.KEY_INPUT_COLUMN_COUNT, mColumnCount);
intent.putExtra(SobotAlbum.KEY_INPUT_ALLOW_CAMERA, mHasCamera);
intent.putExtra(SobotAlbum.KEY_INPUT_LIMIT_COUNT, 1);
intent.putExtra(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY, mFilterVisibility);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_QUALITY, mQuality);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_DURATION, mLimitDuration);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_BYTES, mLimitBytes);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import androidx.annotation.Nullable;
import com.sobot.album.Action;
import com.sobot.album.api.widget.Widget;
public abstract class BasicAlbumWrapper<Returner extends BasicAlbumWrapper, Result, Cancel, Checked> {
final Context mContext;
Action<Result> mResult;
Action<Cancel> mCancel;
Widget mWidget;
Checked mChecked;
BasicAlbumWrapper(Context context) {
this.mContext = context;
mWidget = Widget.getDefaultWidget(context);
}
/**
* Set the action when result.
*
* @param result action when producing result.
*/
public final Returner onResult(Action<Result> result) {
this.mResult = result;
return (Returner) this;
}
/**
* Set the action when canceling.
*
* @param cancel action when canceled.
*/
public final Returner onCancel(Action<Cancel> cancel) {
this.mCancel = cancel;
return (Returner) this;
}
/**
* Set the widget property.
*
* @param widget the widget.
*/
public final Returner widget(@Nullable Widget widget) {
this.mWidget = widget;
return (Returner) this;
}
/**
* Start up.
*/
public abstract void start();
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import androidx.annotation.Nullable;
import com.sobot.album.Action;
public abstract class BasicCameraWrapper<Returner extends BasicCameraWrapper> {
Context mContext;
Action<String> mResult;
Action<String> mCancel;
String mFilePath;
public BasicCameraWrapper(Context context) {
this.mContext = context;
}
/**
* Set the action when result.
*
* @param result action when producing result.
*/
public final Returner onResult(Action<String> result) {
this.mResult = result;
return (Returner) this;
}
/**
* Set the action when canceling.
*
* @param cancel action when canceled.
*/
public final Returner onCancel(Action<String> cancel) {
this.mCancel = cancel;
return (Returner) this;
}
/**
* Set the image storage path.
*
* @param filePath storage path.
*/
public Returner filePath(@Nullable String filePath) {
this.mFilePath = filePath;
return (Returner) this;
}
/**
* Start up.
*/
public abstract void start();
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import androidx.annotation.IntRange;
public abstract class BasicChoiceAlbumWrapper<Returner extends BasicChoiceAlbumWrapper, Result, Cancel, Checked> extends BasicChoiceWrapper<Returner, Result, Cancel, Checked> {
BasicChoiceAlbumWrapper(Context context) {
super(context);
}
int mQuality = 1;
long mLimitDuration = Integer.MAX_VALUE;
long mLimitBytes = Integer.MAX_VALUE;
/**
* Set the quality when taking video, should be 0 or 1. Currently value 0 means low quality, and value 1 means high quality.
*
* @param quality should be 0 or 1.
*/
public Returner cameraVideoQuality(@IntRange(from = 0, to = 1) int quality) {
this.mQuality = quality;
return (Returner) this;
}
/**
* Set the maximum number of seconds to take video.
*
* @param duration seconds.
*/
public Returner cameraVideoLimitDuration(@IntRange(from = 1) long duration) {
this.mLimitDuration = duration;
return (Returner) this;
}
/**
* Set the maximum file size when taking video.
*
* @param bytes the size of the byte.
*/
public Returner cameraVideoLimitBytes(@IntRange(from = 1) long bytes) {
this.mLimitBytes = bytes;
return (Returner) this;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import androidx.annotation.IntRange;
public abstract class BasicChoiceVideoWrapper<Returner extends BasicChoiceVideoWrapper, Result, Cancel, Checked> extends BasicChoiceWrapper<Returner, Result, Cancel, Checked> {
BasicChoiceVideoWrapper(Context context) {
super(context);
}
int mQuality = 1;
long mLimitDuration = Integer.MAX_VALUE;
long mLimitBytes = Integer.MAX_VALUE;
/**
* Set the quality when taking video, should be 0 or 1. Currently value 0 means low quality, and value 1 means high quality.
*
* @param quality should be 0 or 1.
*/
public Returner quality(@IntRange(from = 0, to = 1) int quality) {
this.mQuality = quality;
return (Returner) this;
}
/**
* Specify the maximum allowed recording duration in seconds.
*
* @param duration the maximum number of seconds.
*/
public Returner limitDuration(@IntRange(from = 1) long duration) {
this.mLimitDuration = duration;
return (Returner) this;
}
/**
* Specify the maximum allowed size.
*
* @param bytes the size of the byte.
*/
public Returner limitBytes(@IntRange(from = 1) long bytes) {
this.mLimitBytes = bytes;
return (Returner) this;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import androidx.annotation.IntRange;
import com.sobot.album.Filter;
public abstract class BasicChoiceWrapper<Returner extends BasicChoiceWrapper, Result, Cancel, Checked> extends BasicAlbumWrapper<Returner, Result, Cancel, Checked> {
boolean mHasCamera = true;
int mColumnCount = 2;
Filter<Long> mSizeFilter;
Filter<String> mMimeTypeFilter;
boolean mFilterVisibility = true;
BasicChoiceWrapper(Context context) {
super(context);
}
/**
* Turn on the camera function.
*/
public Returner camera(boolean hasCamera) {
this.mHasCamera = hasCamera;
return (Returner) this;
}
/**
* Sets the number of columns for the page.
*
* @param count the number of columns.
*/
public Returner columnCount(@IntRange(from = 2, to = 4) int count) {
this.mColumnCount = count;
return (Returner) this;
}
/**
* Filter the file size.
*
* @param filter filter.
*/
public Returner filterSize(Filter<Long> filter) {
this.mSizeFilter = filter;
return (Returner) this;
}
/**
* Filter the file extension.
*
* @param filter filter.
*/
public Returner filterMimeType(Filter<String> filter) {
this.mMimeTypeFilter = filter;
return (Returner) this;
}
/**
* The visibility of the filtered file.
*
* @param visibility true is displayed, false is not displayed.
*/
public Returner afterFilterVisibility(boolean visibility) {
this.mFilterVisibility = visibility;
return (Returner) this;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import androidx.annotation.IntRange;
import com.sobot.album.ItemAction;
import java.util.ArrayList;
public abstract class BasicGalleryWrapper<Returner extends BasicGalleryWrapper, Result, Cancel, Checked> extends BasicAlbumWrapper<Returner, ArrayList<Result>, Cancel, ArrayList<Checked>> {
ItemAction<Checked> mItemClick;
ItemAction<Checked> mItemLongClick;
int mCurrentPosition;
boolean mCheckable;
public BasicGalleryWrapper(Context context) {
super(context);
}
/**
* Set the list has been selected.
*
* @param checked the data list.
*/
public final Returner checkedList(ArrayList<Checked> checked) {
this.mChecked = checked;
return (Returner) this;
}
/**
* When the preview item is clicked.
*
* @param click action.
*/
public Returner itemClick(ItemAction<Checked> click) {
this.mItemClick = click;
return (Returner) this;
}
/**
* When the preview item is clicked long.
*
* @param longClick action.
*/
public Returner itemLongClick(ItemAction<Checked> longClick) {
this.mItemLongClick = longClick;
return (Returner) this;
}
/**
* Set the show position of List.
*
* @param currentPosition the current position.
*/
public Returner currentPosition(@IntRange(from = 0, to = Integer.MAX_VALUE) int currentPosition) {
this.mCurrentPosition = currentPosition;
return (Returner) this;
}
/**
* The ability to select pictures.
*
* @param checkable checkBox is provided.
*/
public Returner checkable(boolean checkable) {
this.mCheckable = checkable;
return (Returner) this;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import com.sobot.album.AlbumFile;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.gallery.GalleryAlbumActivity;
public class GalleryAlbumWrapper extends BasicGalleryWrapper<GalleryAlbumWrapper, AlbumFile, String, AlbumFile> {
public GalleryAlbumWrapper(Context context) {
super(context);
}
@Override
public void start() {
GalleryAlbumActivity.sResult = mResult;
GalleryAlbumActivity.sCancel = mCancel;
GalleryAlbumActivity.sClick = mItemClick;
GalleryAlbumActivity.sLongClick = mItemLongClick;
Intent intent = new Intent(mContext, GalleryAlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putParcelableArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST, mChecked);
intent.putExtra(SobotAlbum.KEY_INPUT_CURRENT_POSITION, mCurrentPosition);
intent.putExtra(SobotAlbum.KEY_INPUT_GALLERY_CHECKABLE, mCheckable);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.gallery.GalleryActivity;
public class GalleryWrapper extends BasicGalleryWrapper<GalleryWrapper, String, String, String> {
public GalleryWrapper(Context context) {
super(context);
}
@Override
public void start() {
GalleryActivity.sResult = mResult;
GalleryActivity.sCancel = mCancel;
GalleryActivity.sClick = mItemClick;
GalleryActivity.sLongClick = mItemLongClick;
Intent intent = new Intent(mContext, GalleryActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putStringArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST, mChecked);
intent.putExtra(SobotAlbum.KEY_INPUT_CURRENT_POSITION, mCurrentPosition);
intent.putExtra(SobotAlbum.KEY_INPUT_GALLERY_CHECKABLE, mCheckable);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.camera.CameraActivity;
/**
* <p>Camera wrapper.</p>
* Created by Yan Zhenjie on 2017/4/18.
*/
public class ImageCameraWrapper extends BasicCameraWrapper<ImageCameraWrapper> {
public ImageCameraWrapper(Context context) {
super(context);
}
public void start() {
CameraActivity.sResult = mResult;
CameraActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, CameraActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CAMERA_IMAGE);
intent.putExtra(SobotAlbum.KEY_INPUT_FILE_PATH, mFilePath);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.IntRange;
import com.sobot.album.AlbumFile;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.album.AlbumActivity;
import java.util.ArrayList;
public final class ImageMultipleWrapper extends BasicChoiceWrapper<ImageMultipleWrapper, ArrayList<AlbumFile>, String, ArrayList<AlbumFile>> {
@IntRange(from = 1, to = Integer.MAX_VALUE)
private int mLimitCount = Integer.MAX_VALUE;
public ImageMultipleWrapper(Context context) {
super(context);
}
/**
* Set the list has been selected.
*
* @param checked the data list.
*/
public final ImageMultipleWrapper checkedList(ArrayList<AlbumFile> checked) {
this.mChecked = checked;
return this;
}
/**
* Set the maximum number to be selected.
*
* @param count the maximum number.
*/
public ImageMultipleWrapper selectCount(@IntRange(from = 1, to = Integer.MAX_VALUE) int count) {
this.mLimitCount = count;
return this;
}
@Override
public void start() {
AlbumActivity.sSizeFilter = mSizeFilter;
AlbumActivity.sMimeFilter = mMimeTypeFilter;
AlbumActivity.sResult = mResult;
AlbumActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, AlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putParcelableArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST, mChecked);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CHOICE_IMAGE);
intent.putExtra(SobotAlbum.KEY_INPUT_CHOICE_MODE, SobotAlbum.MODE_MULTIPLE);
intent.putExtra(SobotAlbum.KEY_INPUT_COLUMN_COUNT, mColumnCount);
intent.putExtra(SobotAlbum.KEY_INPUT_ALLOW_CAMERA, mHasCamera);
intent.putExtra(SobotAlbum.KEY_INPUT_LIMIT_COUNT, mLimitCount);
intent.putExtra(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY, mFilterVisibility);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import com.sobot.album.AlbumFile;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.album.AlbumActivity;
import java.util.ArrayList;
public final class ImageSingleWrapper extends BasicChoiceWrapper<ImageSingleWrapper, ArrayList<AlbumFile>, String, AlbumFile> {
public ImageSingleWrapper(Context context) {
super(context);
}
@Override
public void start() {
AlbumActivity.sSizeFilter = mSizeFilter;
AlbumActivity.sMimeFilter = mMimeTypeFilter;
AlbumActivity.sResult = mResult;
AlbumActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, AlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CHOICE_IMAGE);
intent.putExtra(SobotAlbum.KEY_INPUT_CHOICE_MODE, SobotAlbum.MODE_SINGLE);
intent.putExtra(SobotAlbum.KEY_INPUT_COLUMN_COUNT, mColumnCount);
intent.putExtra(SobotAlbum.KEY_INPUT_ALLOW_CAMERA, mHasCamera);
intent.putExtra(SobotAlbum.KEY_INPUT_LIMIT_COUNT, 1);
intent.putExtra(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY, mFilterVisibility);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.IntRange;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.camera.CameraActivity;
/**
* <p>Camera wrapper.</p>
* Created by Yan Zhenjie on 2017/4/18.
*/
public class VideoCameraWrapper extends BasicCameraWrapper<VideoCameraWrapper> {
private int mQuality = 1;
private long mLimitDuration = Integer.MAX_VALUE;
private long mLimitBytes = Integer.MAX_VALUE;
public VideoCameraWrapper(Context context) {
super(context);
}
/**
* Currently value 0 means low quality, suitable for MMS messages, and value 1 means high quality.
*
* @param quality should be 0 or 1.
*/
public VideoCameraWrapper quality(@IntRange(from = 0, to = 1) int quality) {
this.mQuality = quality;
return this;
}
/**
* Specify the maximum allowed recording duration in seconds.
*
* @param duration the maximum number of seconds.
*/
public VideoCameraWrapper limitDuration(@IntRange(from = 1) long duration) {
this.mLimitDuration = duration;
return this;
}
/**
* Specify the maximum allowed size.
*
* @param bytes the size of the byte.
*/
public VideoCameraWrapper limitBytes(@IntRange(from = 1) long bytes) {
this.mLimitBytes = bytes;
return this;
}
public void start() {
CameraActivity.sResult = mResult;
CameraActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, CameraActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CAMERA_VIDEO);
intent.putExtra(SobotAlbum.KEY_INPUT_FILE_PATH, mFilePath);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_QUALITY, mQuality);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_DURATION, mLimitDuration);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_BYTES, mLimitBytes);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import androidx.annotation.IntRange;
import com.sobot.album.AlbumFile;
import com.sobot.album.Filter;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.album.AlbumActivity;
import java.util.ArrayList;
public final class VideoMultipleWrapper extends BasicChoiceVideoWrapper<VideoMultipleWrapper, ArrayList<AlbumFile>, String, ArrayList<AlbumFile>> {
private int mLimitCount = Integer.MAX_VALUE;
private Filter<Long> mDurationFilter;
public VideoMultipleWrapper(Context context) {
super(context);
}
/**
* Set the list has been selected.
*
* @param checked the data list.
*/
public final VideoMultipleWrapper checkedList(ArrayList<AlbumFile> checked) {
this.mChecked = checked;
return this;
}
/**
* Set the maximum number to be selected.
*
* @param count the maximum number.
*/
public VideoMultipleWrapper selectCount(@IntRange(from = 1, to = Integer.MAX_VALUE) int count) {
this.mLimitCount = count;
return this;
}
/**
* Filter video duration.
*
* @param filter filter.
*/
public VideoMultipleWrapper filterDuration(Filter<Long> filter) {
this.mDurationFilter = filter;
return this;
}
@Override
public void start() {
AlbumActivity.sSizeFilter = mSizeFilter;
AlbumActivity.sMimeFilter = mMimeTypeFilter;
AlbumActivity.sDurationFilter = mDurationFilter;
AlbumActivity.sResult = mResult;
AlbumActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, AlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putParcelableArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST, mChecked);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CHOICE_VIDEO);
intent.putExtra(SobotAlbum.KEY_INPUT_CHOICE_MODE, SobotAlbum.MODE_MULTIPLE);
intent.putExtra(SobotAlbum.KEY_INPUT_COLUMN_COUNT, mColumnCount);
intent.putExtra(SobotAlbum.KEY_INPUT_ALLOW_CAMERA, mHasCamera);
intent.putExtra(SobotAlbum.KEY_INPUT_LIMIT_COUNT, mLimitCount);
intent.putExtra(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY, mFilterVisibility);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_QUALITY, mQuality);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_DURATION, mLimitDuration);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_BYTES, mLimitBytes);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api;
import android.content.Context;
import android.content.Intent;
import com.sobot.album.AlbumFile;
import com.sobot.album.Filter;
import com.sobot.album.SobotAlbum;
import com.sobot.album.app.album.AlbumActivity;
import java.util.ArrayList;
public final class VideoSingleWrapper extends BasicChoiceVideoWrapper<VideoSingleWrapper, ArrayList<AlbumFile>, String, AlbumFile> {
private Filter<Long> mDurationFilter;
public VideoSingleWrapper(Context context) {
super(context);
}
/**
* Filter video duration.
*
* @param filter filter.
*/
public VideoSingleWrapper filterDuration(Filter<Long> filter) {
this.mDurationFilter = filter;
return this;
}
@Override
public void start() {
AlbumActivity.sSizeFilter = mSizeFilter;
AlbumActivity.sMimeFilter = mMimeTypeFilter;
AlbumActivity.sDurationFilter = mDurationFilter;
AlbumActivity.sResult = mResult;
AlbumActivity.sCancel = mCancel;
Intent intent = new Intent(mContext, AlbumActivity.class);
intent.putExtra(SobotAlbum.KEY_INPUT_WIDGET, mWidget);
intent.putExtra(SobotAlbum.KEY_INPUT_FUNCTION, SobotAlbum.FUNCTION_CHOICE_VIDEO);
intent.putExtra(SobotAlbum.KEY_INPUT_CHOICE_MODE, SobotAlbum.MODE_SINGLE);
intent.putExtra(SobotAlbum.KEY_INPUT_COLUMN_COUNT, mColumnCount);
intent.putExtra(SobotAlbum.KEY_INPUT_ALLOW_CAMERA, mHasCamera);
intent.putExtra(SobotAlbum.KEY_INPUT_LIMIT_COUNT, 1);
intent.putExtra(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY, mFilterVisibility);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_QUALITY, mQuality);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_DURATION, mLimitDuration);
intent.putExtra(SobotAlbum.KEY_INPUT_CAMERA_BYTES, mLimitBytes);
mContext.startActivity(intent);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api.camera;
import android.content.Context;
import com.sobot.album.api.ImageCameraWrapper;
import com.sobot.album.api.VideoCameraWrapper;
public class AlbumCamera implements Camera<ImageCameraWrapper, VideoCameraWrapper> {
private Context mContext;
public AlbumCamera(Context context) {
mContext = context;
}
@Override
public ImageCameraWrapper image() {
return new ImageCameraWrapper(mContext);
}
@Override
public VideoCameraWrapper video() {
return new VideoCameraWrapper(mContext);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api.camera;
public interface Camera<Image, Video> {
/**
* Take picture.
*/
Image image();
/**
* Take video.
*/
Video video();
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api.choice;
import android.content.Context;
import com.sobot.album.api.AlbumMultipleWrapper;
import com.sobot.album.api.AlbumSingleWrapper;
public final class AlbumChoice implements Choice<AlbumMultipleWrapper, AlbumSingleWrapper> {
private Context mContext;
public AlbumChoice(Context context) {
mContext = context;
}
@Override
public AlbumMultipleWrapper multipleChoice() {
return new AlbumMultipleWrapper(mContext);
}
@Override
public AlbumSingleWrapper singleChoice() {
return new AlbumSingleWrapper(mContext);
}
}
\ No newline at end of file
package com.sobot.album.api.choice;
public interface Choice<Multiple, Single> {
/**
* Multiple choice.
*/
Multiple multipleChoice();
/**
* Single choice.
*/
Single singleChoice();
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api.choice;
import android.content.Context;
import com.sobot.album.api.ImageMultipleWrapper;
import com.sobot.album.api.ImageSingleWrapper;
public final class ImageChoice implements Choice<ImageMultipleWrapper, ImageSingleWrapper> {
private Context mContext;
public ImageChoice(Context context) {
mContext = context;
}
@Override
public ImageMultipleWrapper multipleChoice() {
return new ImageMultipleWrapper(mContext);
}
@Override
public ImageSingleWrapper singleChoice() {
return new ImageSingleWrapper(mContext);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api.choice;
import android.content.Context;
import com.sobot.album.api.VideoMultipleWrapper;
import com.sobot.album.api.VideoSingleWrapper;
public final class VideoChoice implements Choice<VideoMultipleWrapper, VideoSingleWrapper> {
private Context mContext;
public VideoChoice(Context context) {
mContext = context;
}
@Override
public VideoMultipleWrapper multipleChoice() {
return new VideoMultipleWrapper(mContext);
}
@Override
public VideoSingleWrapper singleChoice() {
return new VideoSingleWrapper(mContext);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.api.widget;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import com.sobot.album.R;
import com.sobot.album.util.AlbumUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class Widget implements Parcelable {
public static final int STYLE_LIGHT = 1;
public static final int STYLE_DARK = 2;
@IntDef({STYLE_DARK, STYLE_LIGHT})
@Retention(RetentionPolicy.SOURCE)
public @interface UiStyle {
}
/**
* Use when the status bar and the Toolbar are dark.
*/
public static Builder newDarkBuilder(Context context) {
return new Builder(context, STYLE_DARK);
}
/**
* Use when the status bar and the Toolbar are light.
*/
public static Builder newLightBuilder(Context context) {
return new Builder(context, STYLE_LIGHT);
}
private Context mContext;
private int mUiStyle;
private int mStatusBarColor;
private int mToolBarColor;
private int mNavigationBarColor;
private String mTitle;
private ColorStateList mMediaItemCheckSelector;
private ColorStateList mBucketItemCheckSelector;
private ButtonStyle mButtonStyle;
private Widget(Builder builder) {
this.mContext = builder.mContext;
this.mUiStyle = builder.mUiStyle;
this.mStatusBarColor = builder.mStatusBarColor == 0 ? getColor(R.color.albumColorPrimaryDark) : builder.mStatusBarColor;
this.mToolBarColor = builder.mToolBarColor == 0 ? getColor(R.color.albumColorPrimary) : builder.mToolBarColor;
this.mNavigationBarColor = builder.mNavigationBarColor == 0 ? getColor(R.color.albumColorPrimaryBlack) : builder.mNavigationBarColor;
this.mTitle = TextUtils.isEmpty(builder.mTitle) ? mContext.getString(R.string.sobot_album_title) : builder.mTitle;
this.mMediaItemCheckSelector = builder.mMediaItemCheckSelector == null ? AlbumUtils.getColorStateList(getColor(R.color.albumSelectorNormal), getColor(R.color.albumColorPrimary)) : builder.mMediaItemCheckSelector;
this.mBucketItemCheckSelector = builder.mBucketItemCheckSelector == null ? AlbumUtils.getColorStateList(getColor(R.color.albumSelectorNormal), getColor(R.color.albumColorPrimary)) : builder.mBucketItemCheckSelector;
this.mButtonStyle = builder.mButtonStyle == null ? ButtonStyle.newDarkBuilder(mContext).build() : builder.mButtonStyle;
}
@UiStyle
public int getUiStyle() {
return mUiStyle;
}
@ColorInt
public int getStatusBarColor() {
return mStatusBarColor;
}
@ColorInt
public int getToolBarColor() {
return mToolBarColor;
}
@ColorInt
public int getNavigationBarColor() {
return mNavigationBarColor;
}
public String getTitle() {
return mTitle;
}
public ColorStateList getMediaItemCheckSelector() {
return mMediaItemCheckSelector;
}
public ColorStateList getBucketItemCheckSelector() {
return mBucketItemCheckSelector;
}
public ButtonStyle getButtonStyle() {
return mButtonStyle;
}
protected Widget(Parcel in) {
mUiStyle = in.readInt();
mStatusBarColor = in.readInt();
mToolBarColor = in.readInt();
mNavigationBarColor = in.readInt();
mTitle = in.readString();
mMediaItemCheckSelector = in.readParcelable(ColorStateList.class.getClassLoader());
mBucketItemCheckSelector = in.readParcelable(ColorStateList.class.getClassLoader());
mButtonStyle = in.readParcelable(ButtonStyle.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mUiStyle);
dest.writeInt(mStatusBarColor);
dest.writeInt(mToolBarColor);
dest.writeInt(mNavigationBarColor);
dest.writeString(mTitle);
dest.writeParcelable(mMediaItemCheckSelector, flags);
dest.writeParcelable(mBucketItemCheckSelector, flags);
dest.writeParcelable(mButtonStyle, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Widget> CREATOR = new Creator<Widget>() {
@Override
public Widget createFromParcel(Parcel in) {
return new Widget(in);
}
@Override
public Widget[] newArray(int size) {
return new Widget[size];
}
};
private int getColor(int colorId) {
return ContextCompat.getColor(mContext, colorId);
}
public static class Builder {
private Context mContext;
private int mUiStyle;
private int mStatusBarColor;
private int mToolBarColor;
private int mNavigationBarColor;
private String mTitle;
private ColorStateList mMediaItemCheckSelector;
private ColorStateList mBucketItemCheckSelector;
private ButtonStyle mButtonStyle;
private Builder(Context context, @UiStyle int style) {
this.mContext = context;
this.mUiStyle = style;
}
/**
* Status bar color.
*/
public Builder statusBarColor(@ColorInt int color) {
this.mStatusBarColor = color;
return this;
}
/**
* Toolbar color.
*/
public Builder toolBarColor(@ColorInt int color) {
this.mToolBarColor = color;
return this;
}
/**
* Virtual navigation bar.
*/
public Builder navigationBarColor(@ColorInt int color) {
this.mNavigationBarColor = color;
return this;
}
/**
* Set the title of the Toolbar.
*/
public Builder title(@StringRes int title) {
return title(mContext.getString(title));
}
/**
* Set the title of the Toolbar.
*/
public Builder title(String title) {
this.mTitle = title;
return this;
}
/**
* The color of the {@code Media Item} selector.
*/
public Builder mediaItemCheckSelector(@ColorInt int normalColor, @ColorInt int highLightColor) {
this.mMediaItemCheckSelector = AlbumUtils.getColorStateList(normalColor, highLightColor);
return this;
}
/**
* The color of the {@code Bucket Item} selector.
*/
public Builder bucketItemCheckSelector(@ColorInt int normalColor, @ColorInt int highLightColor) {
this.mBucketItemCheckSelector = AlbumUtils.getColorStateList(normalColor, highLightColor);
return this;
}
/**
* Set the style of the Button.
*/
public Builder buttonStyle(ButtonStyle buttonStyle) {
this.mButtonStyle = buttonStyle;
return this;
}
/**
* Create target.
*/
public Widget build() {
return new Widget(this);
}
}
public static class ButtonStyle implements Parcelable {
/**
* Use when the Button are dark.
*/
public static Builder newDarkBuilder(Context context) {
return new Builder(context, STYLE_DARK);
}
/**
* Use when the Button are light.
*/
public static Builder newLightBuilder(Context context) {
return new Builder(context, STYLE_LIGHT);
}
private Context mContext;
private int mUiStyle;
private ColorStateList mButtonSelector;
private ButtonStyle(Builder builder) {
this.mContext = builder.mContext;
this.mUiStyle = builder.mUiStyle;
this.mButtonSelector = builder.mButtonSelector == null ?
AlbumUtils.getColorStateList(ContextCompat.getColor(mContext, R.color.albumColorPrimary),
ContextCompat.getColor(mContext, R.color.albumColorPrimaryDark)
) :
builder.mButtonSelector;
}
public int getUiStyle() {
return mUiStyle;
}
public ColorStateList getButtonSelector() {
return mButtonSelector;
}
protected ButtonStyle(Parcel in) {
mUiStyle = in.readInt();
mButtonSelector = in.readParcelable(ColorStateList.class.getClassLoader());
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mUiStyle);
dest.writeParcelable(mButtonSelector, flags);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<ButtonStyle> CREATOR = new Creator<ButtonStyle>() {
@Override
public ButtonStyle createFromParcel(Parcel in) {
return new ButtonStyle(in);
}
@Override
public ButtonStyle[] newArray(int size) {
return new ButtonStyle[size];
}
};
public static class Builder {
private Context mContext;
private int mUiStyle;
private ColorStateList mButtonSelector;
private Builder(Context context, @UiStyle int style) {
this.mContext = context;
this.mUiStyle = style;
}
/**
* Set button click effect.
*
* @param normalColor normal color.
* @param highLightColor feedback color.
*/
public Builder setButtonSelector(@ColorInt int normalColor, @ColorInt int highLightColor) {
mButtonSelector = AlbumUtils.getColorStateList(normalColor, highLightColor);
return this;
}
public ButtonStyle build() {
return new ButtonStyle(this);
}
}
}
/**
* Create default widget.
*/
public static Widget getDefaultWidget(Context context) {
return Widget.newDarkBuilder(context)
.statusBarColor(ContextCompat.getColor(context, R.color.albumColorPrimaryDark))
.toolBarColor(ContextCompat.getColor(context, R.color.albumColorPrimary))
.navigationBarColor(ContextCompat.getColor(context, R.color.albumColorPrimaryBlack))
.title(R.string.sobot_album_title)
.mediaItemCheckSelector(ContextCompat.getColor(context, R.color.albumSelectorNormal),
ContextCompat.getColor(context, R.color.albumColorPrimary))
.bucketItemCheckSelector(ContextCompat.getColor(context, R.color.albumSelectorNormal),
ContextCompat.getColor(context, R.color.albumColorPrimary))
.buttonStyle(
ButtonStyle.newDarkBuilder(context)
.setButtonSelector(ContextCompat.getColor(context, R.color.albumColorPrimary),
ContextCompat.getColor(context, R.color.albumColorPrimaryDark))
.build()
)
.build();
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.app;
import android.app.Activity;
import android.content.res.Configuration;
import android.view.View;
import android.widget.CompoundButton;
import com.sobot.album.AlbumFolder;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.mvp.BasePresenter;
import com.sobot.album.mvp.BaseView;
import java.util.List;
public final class Contract {
public interface AlbumPresenter extends BasePresenter {
/**
* Click the folder switch.
*/
void clickFolderSwitch();
/**
* Click camera.
*/
void clickCamera(View v);
/**
* Try to check item.
*
* @param button view.
* @param position position of item.
*/
void tryCheckItem(CompoundButton button, int position);
/**
* Try to preview item.
*
* @param position position of item.
*/
void tryPreviewItem(int position);
/**
* Preview the checked items.
*/
void tryPreviewChecked();
/**
* Complete.
*/
void complete();
}
public static abstract class AlbumView extends BaseView<AlbumPresenter> {
public AlbumView(Activity activity, AlbumPresenter presenter) {
super(activity, presenter);
}
/**
* Set some properties of the view.
*
* @param widget {@link Widget}.
* @param column the count of columns.
* @param hasCamera the camera is enabled.
* @param choiceMode choice mode, one of {@link SobotAlbum#FUNCTION_CHOICE_ALBUM}, {@link
* SobotAlbum#FUNCTION_CHOICE_IMAGE} or {@link SobotAlbum#FUNCTION_CHOICE_VIDEO}.
*/
public abstract void setupViews(Widget widget, int column, boolean hasCamera, int choiceMode);
/**
* Set the loading visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setLoadingDisplay(boolean display);
/**
* Should be re-layout.
*
* @param newConfig config.
*/
public abstract void onConfigurationChanged(Configuration newConfig);
/**
* Set the complete menu visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setCompleteDisplay(boolean display);
/**
* Bind folder.
*
* @param albumFolder {@link AlbumFolder}.
*/
public abstract void bindAlbumFolder(AlbumFolder albumFolder);
/**
* Notify item was inserted.
*
* @param position position of item.
*/
public abstract void notifyInsertItem(int position);
/**
* Notify item was changed.
*
* @param position position of item.
*/
public abstract void notifyItem(int position);
/**
* Set checked count.
*
* @param count the number of items checked.
*/
public abstract void setCheckedCount(int count);
}
public interface NullPresenter extends BasePresenter {
/**
* Take a picture.
*/
void takePicture();
/**
* Take a video.
*/
void takeVideo();
}
public static abstract class NullView extends BaseView<NullPresenter> {
public NullView(Activity activity, NullPresenter presenter) {
super(activity, presenter);
}
/**
* Set some properties of the view.
*
* @param widget {@link Widget}.
*/
public abstract void setupViews(Widget widget);
/**
* Set the message of page.
*
* @param message message.
*/
public abstract void setMessage(int message);
/**
* Set the button visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setMakeImageDisplay(boolean display);
/**
* Set the button visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setMakeVideoDisplay(boolean display);
}
public interface GalleryPresenter extends BasePresenter {
void clickItem(int position);
void longClickItem(int position);
/**
* Set the current position of item .
*/
void onCurrentChanged(int position);
/**
* Try to check the current item.
*/
void onCheckedChanged();
/**
* Complete.
*/
void complete();
}
public static abstract class GalleryView<Data> extends BaseView<GalleryPresenter> {
public GalleryView(Activity activity, GalleryPresenter presenter) {
super(activity, presenter);
}
/**
* Set some properties of the view.
*
* @param widget {@link Widget}.
* @param checkable show the checkbox.
*/
public abstract void setupViews(Widget widget, boolean checkable);
/**
* Bind data.
*
* @param dataList data.
*/
public abstract void bindData(List<Data> dataList);
/**
* Set the position of the item to be displayed.
*
* @param position position.
*/
public abstract void setCurrentItem(int position);
/**
* Set duration visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setDurationDisplay(boolean display);
/**
* Set duration.
*
* @param duration duration.
*/
public abstract void setDuration(String duration);
/**
* Changes the checked state of this button.
*
* @param checked true to check the button, false to uncheck it.
*/
public abstract void setChecked(boolean checked);
/**
* Set bottom visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setBottomDisplay(boolean display);
/**
* Set layer visibility.
*
* @param display true is displayed, otherwise it is not displayed.
*/
public abstract void setLayerDisplay(boolean display);
/**
* Set the complete button text.
*
* @param text text.
*/
public abstract void setCompleteText(String text);
public void setShowTitle(boolean previewMySelect) {
}
}
}
\ No newline at end of file
package com.sobot.album.app.album;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import android.widget.CompoundButton;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.PopupMenu;
import androidx.core.content.ContextCompat;
import com.sobot.album.Action;
import com.sobot.album.AlbumFile;
import com.sobot.album.AlbumFolder;
import com.sobot.album.Filter;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.app.album.data.MediaReadTask;
import com.sobot.album.app.album.data.MediaReader;
import com.sobot.album.app.album.data.PathConversion;
import com.sobot.album.app.album.data.PathConvertTask;
import com.sobot.album.app.album.data.ThumbnailBuildTask;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.mediascanner.MediaScanner;
import com.sobot.album.util.AlbumUtils;
import com.sobot.album.widget.LoadingDialog;
import com.sobot.utils.SobotLogUtils;
import com.sobot.widget.ui.base.SobotBaseActivity;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class AlbumActivity extends SobotBaseActivity implements
Contract.AlbumPresenter,
MediaReadTask.Callback,
GalleryActivity.Callback,
PathConvertTask.Callback,
ThumbnailBuildTask.Callback {
private static final int CODE_ACTIVITY_NULL = 1;
private static final int CODE_PERMISSION_STORAGE = 1;
public static Filter<Long> sSizeFilter;
public static Filter<String> sMimeFilter;
public static Filter<Long> sDurationFilter;
public static Action<ArrayList<AlbumFile>> sResult;
public static Action<String> sCancel;
private List<AlbumFolder> mAlbumFolders;
private int mCurrentFolder;
private Widget mWidget;
private int mFunction;
private int mChoiceMode;
private int mColumnCount=15;
private boolean mHasCamera;
private int mLimitCount;
private int mQuality;
private long mLimitDuration;
private long mLimitBytes;
private boolean mFilterVisibility;
private ArrayList<AlbumFile> mCheckedList;
private MediaScanner mMediaScanner;
private Contract.AlbumView mView;
private FolderDialog mFolderDialog;
private PopupMenu mCameraPopupMenu;
private LoadingDialog mLoadingDialog;
private MediaReadTask mMediaReadTask;
private String limitText="";
@Override
protected void onCreate(Bundle savedInstanceState) {
changeAppLanguage();
initializeArgument();
limitText=getResources().getQuantityString(R.plurals.album_check_image_limit, mLimitCount, mLimitCount);
super.onCreate(savedInstanceState);
}
@Override
protected int getContentViewResId() {
return R.layout.sobot_album_activity_album;
}
@Override
protected void initView() throws InterruptedException {
setTitle(R.string.sobot_album_select_images);
getLeftMenu().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callbackCancel();
}
});
mView = new AlbumView(this, this);
mView.setupViews(mWidget, mColumnCount, mHasCamera, mChoiceMode);
mView.setTitle(mWidget.getTitle());
mView.setCompleteDisplay(false);
mView.setLoadingDisplay(true);
}
@Override
protected void initData() {
/*this.permissionListener = new SobotPermissionListenerImpl() {
public void onPermissionSuccessListener() {
ArrayList<AlbumFile> checkedList = getIntent().getParcelableArrayListExtra(Album.KEY_INPUT_CHECKED_LIST);
MediaReader mediaReader = new MediaReader(AlbumActivity.this, sSizeFilter, sMimeFilter, sDurationFilter, mFilterVisibility);
mMediaReadTask = new MediaReadTask(mFunction, checkedList, mediaReader, AlbumActivity.this);
mMediaReadTask.execute();
}
};
if (!this.checkIsShowPermissionPop(this.getString(R.string.sobot_memory_card), this.getString(R.string.sobot_memory_card_yongtu), 1, 4)) {
if (this.checkStoragePermission(4)) {
ArrayList<AlbumFile> checkedList = getIntent().getParcelableArrayListExtra(Album.KEY_INPUT_CHECKED_LIST);
MediaReader mediaReader = new MediaReader(this, sSizeFilter, sMimeFilter, sDurationFilter, mFilterVisibility);
mMediaReadTask = new MediaReadTask(mFunction, checkedList, mediaReader, this);
mMediaReadTask.execute();
}
}*/
//是否是获取了选取部分的图片
if(ContextCompat.checkSelfPermission(AlbumActivity.this,Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)==PackageManager.PERMISSION_GRANTED) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
ArrayList<AlbumFile> checkedList = getIntent().getParcelableArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST);
MediaReader mediaReader = new MediaReader(AlbumActivity.this, sSizeFilter, sMimeFilter, sDurationFilter, mFilterVisibility);
mMediaReadTask = new MediaReadTask(mFunction, checkedList, mediaReader, AlbumActivity.this);
mMediaReadTask.execute();
}
}, 2000);
}else{
ArrayList<AlbumFile> checkedList = getIntent().getParcelableArrayListExtra(SobotAlbum.KEY_INPUT_CHECKED_LIST);
MediaReader mediaReader = new MediaReader(AlbumActivity.this, sSizeFilter, sMimeFilter, sDurationFilter, mFilterVisibility);
mMediaReadTask = new MediaReadTask(mFunction, checkedList, mediaReader, AlbumActivity.this);
mMediaReadTask.execute();
}
}
private void initializeArgument() {
Bundle argument = getIntent().getExtras();
assert argument != null;
mWidget = argument.getParcelable(SobotAlbum.KEY_INPUT_WIDGET);
mFunction = argument.getInt(SobotAlbum.KEY_INPUT_FUNCTION);
mChoiceMode = argument.getInt(SobotAlbum.KEY_INPUT_CHOICE_MODE);
mColumnCount = argument.getInt(SobotAlbum.KEY_INPUT_COLUMN_COUNT);
mHasCamera = argument.getBoolean(SobotAlbum.KEY_INPUT_ALLOW_CAMERA);
mLimitCount = argument.getInt(SobotAlbum.KEY_INPUT_LIMIT_COUNT);
mQuality = argument.getInt(SobotAlbum.KEY_INPUT_CAMERA_QUALITY);
mLimitDuration = argument.getLong(SobotAlbum.KEY_INPUT_CAMERA_DURATION);
mLimitBytes = argument.getLong(SobotAlbum.KEY_INPUT_CAMERA_BYTES);
mFilterVisibility = argument.getBoolean(SobotAlbum.KEY_INPUT_FILTER_VISIBILITY);
}
/**
* Use different layouts depending on the style.
*
* @return layout id.
*/
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mView.onConfigurationChanged(newConfig);
if (mFolderDialog != null && !mFolderDialog.isShowing()) mFolderDialog = null;
}
@Override
public void onScanCallback(ArrayList<AlbumFolder> albumFolders, ArrayList<AlbumFile> checkedFiles) {
mMediaReadTask = null;
switch (mChoiceMode) {
case SobotAlbum.MODE_MULTIPLE: {
mView.setCompleteDisplay(true);
break;
}
case SobotAlbum.MODE_SINGLE: {
mView.setCompleteDisplay(false);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
mView.setLoadingDisplay(false);
mAlbumFolders = albumFolders;
mCheckedList = checkedFiles;
if (mAlbumFolders.get(0).getAlbumFiles().isEmpty()) {
Intent intent = new Intent(this, NullActivity.class);
intent.putExtras(getIntent());
startActivityForResult(intent, CODE_ACTIVITY_NULL);
} else {
showFolderAlbumFiles(0);
int count = mCheckedList.size();
mView.setCheckedCount(count);
mView.setSubTitle(count + "/" + mLimitCount);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CODE_ACTIVITY_NULL: {
if (resultCode == RESULT_OK) {
String imagePath = NullActivity.parsePath(data);
String mimeType = AlbumUtils.getMimeType(imagePath);
if (!TextUtils.isEmpty(mimeType)) mCameraAction.onAction(imagePath);
} else {
callbackCancel();
}
break;
}
}
}
@Override
public void clickFolderSwitch() {
if (mFolderDialog == null) {
mFolderDialog = new FolderDialog(this, mWidget, mAlbumFolders, new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
mCurrentFolder = position;
showFolderAlbumFiles(mCurrentFolder);
}
});
}
if (!mFolderDialog.isShowing()) mFolderDialog.show();
}
/**
* Update data source.
*/
private void showFolderAlbumFiles(int position) {
this.mCurrentFolder = position;
AlbumFolder albumFolder = mAlbumFolders.get(position);
mView.bindAlbumFolder(albumFolder);
}
@Override
public void clickCamera(View v) {
int hasCheckSize = mCheckedList.size();
if (hasCheckSize >= mLimitCount) {
int messageRes;
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
messageRes = R.plurals.album_check_image_limit_camera;
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
messageRes = R.plurals.album_check_video_limit_camera;
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
messageRes = R.plurals.album_check_album_limit_camera;
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
mView.toast(getResources().getQuantityString(messageRes, mLimitCount, mLimitCount));
} else {
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
takePicture();
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
takeVideo();
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
if (mCameraPopupMenu == null) {
mCameraPopupMenu = new PopupMenu(this, v);
// mCameraPopupMenu.getMenuInflater().inflate(R.menu.album_menu_item_camera, mCameraPopupMenu.getMenu());
// mCameraPopupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
// @Override
// public boolean onMenuItemClick(MenuItem item) {
// int id = item.getItemId();
// if (id == R.id.album_menu_camera_image) {
// takePicture();
// } else if (id == R.id.album_menu_camera_video) {
// takeVideo();
// }
// return true;
// }
// });
}
mCameraPopupMenu.show();
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
}
private void takePicture() {
String filePath;
if (mCurrentFolder == 0) {
filePath = AlbumUtils.randomJPGPath();
} else {
File file = new File(mAlbumFolders.get(mCurrentFolder).getAlbumFiles().get(0).getPath());
filePath = AlbumUtils.randomJPGPath(file.getParentFile());
}
SobotAlbum.camera(this)
.image()
.filePath(filePath)
.onResult(mCameraAction)
.start();
}
private void takeVideo() {
String filePath;
if (mCurrentFolder == 0) {
filePath = AlbumUtils.randomMP4Path();
} else {
File file = new File(mAlbumFolders.get(mCurrentFolder).getAlbumFiles().get(0).getPath());
filePath = AlbumUtils.randomMP4Path(file.getParentFile());
}
SobotAlbum.camera(this)
.video()
.filePath(filePath)
.quality(mQuality)
.limitDuration(mLimitDuration)
.limitBytes(mLimitBytes)
.onResult(mCameraAction)
.start();
}
private Action<String> mCameraAction = new Action<String>() {
@Override
public void onAction(@NonNull String result) {
if (mMediaScanner == null) {
mMediaScanner = new MediaScanner(AlbumActivity.this);
}
mMediaScanner.scan(result);
PathConversion conversion = new PathConversion(sSizeFilter, sMimeFilter, sDurationFilter);
PathConvertTask task = new PathConvertTask(conversion, AlbumActivity.this);
task.execute(result);
}
};
@Override
public void onConvertStart() {
showLoadingDialog();
mLoadingDialog.setMessage(R.string.album_converting);
}
@Override
public void onConvertCallback(AlbumFile albumFile) {
albumFile.setChecked(!albumFile.isDisable());
if (albumFile.isDisable()) {
if (mFilterVisibility) addFileToList(albumFile);
else mView.toast(getString(R.string.album_take_file_unavailable));
} else {
addFileToList(albumFile);
}
dismissLoadingDialog();
}
private void addFileToList(AlbumFile albumFile) {
if (mCurrentFolder != 0) {
List<AlbumFile> albumFiles = mAlbumFolders.get(0).getAlbumFiles();
if (albumFiles.size() > 0) albumFiles.add(0, albumFile);
else albumFiles.add(albumFile);
}
AlbumFolder albumFolder = mAlbumFolders.get(mCurrentFolder);
List<AlbumFile> albumFiles = albumFolder.getAlbumFiles();
if (albumFiles.isEmpty()) {
albumFiles.add(albumFile);
mView.bindAlbumFolder(albumFolder);
} else {
albumFiles.add(0, albumFile);
mView.notifyInsertItem(mHasCamera ? 1 : 0);
}
mCheckedList.add(albumFile);
int count = mCheckedList.size();
mView.setCheckedCount(count);
mView.setSubTitle(count + "/" + mLimitCount);
switch (mChoiceMode) {
case SobotAlbum.MODE_SINGLE: {
callbackResult();
break;
}
case SobotAlbum.MODE_MULTIPLE: {
// Nothing.
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
@Override
public void tryCheckItem(CompoundButton button, int position) {
AlbumFile albumFile = mAlbumFolders.get(mCurrentFolder).getAlbumFiles().get(position);
if (button.isChecked()) {
if (mCheckedList.size() >= mLimitCount) {
int messageRes;
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
messageRes = R.plurals.album_check_image_limit;
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
messageRes = R.plurals.album_check_video_limit;
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
messageRes = R.plurals.album_check_album_limit;
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
mView.toast(limitText);
button.setChecked(false);
} else {
albumFile.setChecked(true);
mCheckedList.add(albumFile);
setCheckedCount();
}
} else {
albumFile.setChecked(false);
mCheckedList.remove(albumFile);
setCheckedCount();
}
}
private void setCheckedCount() {
int count = mCheckedList.size();
mView.setCheckedCount(count);
mView.setSubTitle(count + "/" + mLimitCount);
}
@Override
public void tryPreviewItem(int position) {
switch (mChoiceMode) {
case SobotAlbum.MODE_SINGLE: {
AlbumFile albumFile = mAlbumFolders.get(mCurrentFolder).getAlbumFiles().get(position);
// albumFile.setChecked(true);
// mView.notifyItem(position);
mCheckedList.add(albumFile);
setCheckedCount();
callbackResult();
break;
}
case SobotAlbum.MODE_MULTIPLE: {
GalleryActivity.sAlbumFiles = mAlbumFolders.get(mCurrentFolder).getAlbumFiles();
SobotLogUtils.d("======全相册===="+mAlbumFolders.get(mCurrentFolder).getAlbumFiles().size());
GalleryActivity.sCheckedCount = mCheckedList.size();
GalleryActivity.sCurrentPosition = position;
GalleryActivity.sCallback = this;
GalleryActivity.previewMySelect = false;
//预览全相册
Intent intent = new Intent(this, GalleryActivity.class);
intent.putExtras(getIntent());
startActivity(intent);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
@Override
public void tryPreviewChecked() {
if (mCheckedList.size() > 0) {
//预览已选中的
GalleryActivity.sAlbumFiles = new ArrayList<>(mCheckedList);
GalleryActivity.sCheckedCount = mCheckedList.size();
GalleryActivity.sCurrentPosition = 0;
GalleryActivity.sCallback = this;
GalleryActivity.previewMySelect = true;
Intent intent = new Intent(this, GalleryActivity.class);
intent.putExtras(getIntent());
startActivity(intent);
}
}
@Override
public void onPreviewComplete() {
callbackResult();
}
@Override
public void onPreviewChanged(AlbumFile albumFile) {
ArrayList<AlbumFile> albumFiles = mAlbumFolders.get(mCurrentFolder).getAlbumFiles();
int position = albumFiles.indexOf(albumFile);
int notifyPosition = mHasCamera ? position + 1 : position;
mView.notifyItem(notifyPosition);
if (albumFile.isChecked()) {
if (!mCheckedList.contains(albumFile)) mCheckedList.add(albumFile);
} else {
if (mCheckedList.contains(albumFile)) mCheckedList.remove(albumFile);
}
setCheckedCount();
}
@Override
public void complete() {
if (mCheckedList.isEmpty()) {
int messageRes;
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
messageRes = R.string.album_check_image_little;
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
messageRes = R.string.album_check_video_little;
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
messageRes = R.string.album_check_album_little;
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
mView.toast(messageRes);
} else {
callbackResult();
}
}
@Override
public void onBackPressed() {
if (mMediaReadTask != null) mMediaReadTask.cancel(true);
callbackCancel();
}
/**
* Callback result action.
*/
private void callbackResult() {
ThumbnailBuildTask task = new ThumbnailBuildTask(this, mCheckedList, this);
task.execute();
}
@Override
public void onThumbnailStart() {
showLoadingDialog();
mLoadingDialog.setMessage(R.string.album_thumbnail);
}
@Override
public void onThumbnailCallback(ArrayList<AlbumFile> albumFiles) {
if (sResult != null) sResult.onAction(albumFiles);
dismissLoadingDialog();
finish();
}
/**
* Callback cancel action.
*/
private void callbackCancel() {
if (sCancel != null) sCancel.onAction("User canceled.");
finish();
}
/**
* Display loading dialog.
*/
private void showLoadingDialog() {
if (mLoadingDialog == null) {
mLoadingDialog = new LoadingDialog(this);
mLoadingDialog.setupViews(mWidget);
}
if (!mLoadingDialog.isShowing()) {
// mLoadingDialog.show();
}
}
/**
* Dismiss loading dialog.
*/
public void dismissLoadingDialog() {
if (mLoadingDialog != null && mLoadingDialog.isShowing()) {
mLoadingDialog.dismiss();
}
}
@Override
public void finish() {
sSizeFilter = null;
sMimeFilter = null;
sDurationFilter = null;
sResult = null;
sCancel = null;
super.finish();
}
@Override
public void bye() {
}
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album.app.album;
import android.content.Context;
import android.content.res.ColorStateList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.impl.OnCheckedClickListener;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.util.AlbumUtils;
import java.util.List;
/**
* <p>Picture list display adapter.</p>
* Created by Yan Zhenjie on 2016/10/18.
*/
public class AlbumAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_BUTTON = 1;
private static final int TYPE_IMAGE = 2;
private static final int TYPE_VIDEO = 3;
private final LayoutInflater mInflater;
private final boolean hasCamera;
private final int mChoiceMode;
private final ColorStateList mSelector;
private List<AlbumFile> mAlbumFiles;
private OnItemClickListener mAddPhotoClickListener;
private OnItemClickListener mItemClickListener;
private OnCheckedClickListener mCheckedClickListener;
private Context mContext;
public AlbumAdapter(Context context, boolean hasCamera, int choiceMode, ColorStateList selector) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
this.hasCamera = hasCamera;
this.mChoiceMode = choiceMode;
this.mSelector = selector;
}
public void setAlbumFiles(List<AlbumFile> albumFiles) {
this.mAlbumFiles = albumFiles;
}
public void setAddClickListener(OnItemClickListener addPhotoClickListener) {
this.mAddPhotoClickListener = addPhotoClickListener;
}
public void setItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;
}
public void setCheckedClickListener(OnCheckedClickListener checkedClickListener) {
this.mCheckedClickListener = checkedClickListener;
}
@Override
public int getItemCount() {
int camera = hasCamera ? 1 : 0;
return mAlbumFiles == null ? camera : mAlbumFiles.size() + camera;
}
@Override
public int getItemViewType(int position) {
switch (position) {
case 0: {
if (hasCamera) {
return TYPE_BUTTON;
} else {
AlbumFile albumFile = mAlbumFiles.get(position);
return albumFile.getMediaType() == AlbumFile.TYPE_VIDEO ? TYPE_VIDEO : TYPE_IMAGE;
}
}
default: {
position = hasCamera ? position - 1 : position;
AlbumFile albumFile = mAlbumFiles.get(position);
return albumFile.getMediaType() == AlbumFile.TYPE_VIDEO ? TYPE_VIDEO : TYPE_IMAGE;
}
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_BUTTON: {
return new ButtonViewHolder(mInflater.inflate(R.layout.album_item_content_button, parent, false), mAddPhotoClickListener);
}
case TYPE_IMAGE: {
ImageHolder imageViewHolder = new ImageHolder(mContext, mInflater.inflate(R.layout.album_item_content_image, parent, false),
hasCamera,
mItemClickListener,
mCheckedClickListener);
if (mChoiceMode == SobotAlbum.MODE_MULTIPLE) {
imageViewHolder.mCheckBox.setVisibility(View.VISIBLE);
imageViewHolder.mCheckBox.setTextColor(mSelector);
} else {
imageViewHolder.mCheckBox.setVisibility(View.GONE);
}
return imageViewHolder;
}
case TYPE_VIDEO: {
VideoHolder videoViewHolder = new VideoHolder(mContext, mInflater.inflate(R.layout.album_item_content_video, parent, false),
hasCamera,
mItemClickListener,
mCheckedClickListener);
if (mChoiceMode == SobotAlbum.MODE_MULTIPLE) {
videoViewHolder.mCheckBox.setVisibility(View.VISIBLE);
videoViewHolder.mCheckBox.setTextColor(mSelector);
} else {
videoViewHolder.mCheckBox.setVisibility(View.GONE);
}
return videoViewHolder;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case TYPE_BUTTON: {
// Nothing.
break;
}
case TYPE_IMAGE:
case TYPE_VIDEO: {
MediaViewHolder mediaHolder = (MediaViewHolder) holder;
int camera = hasCamera ? 1 : 0;
position = holder.getAdapterPosition() - camera;
AlbumFile albumFile = mAlbumFiles.get(position);
if (albumFile != null && albumFile.getSize() >= 50 * 1024 * 1024) {
albumFile.setDisable(true);
} else if (albumFile != null) {
albumFile.setDisable(false);
}
mediaHolder.setData(albumFile);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
private static class ButtonViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final OnItemClickListener mItemClickListener;
ButtonViewHolder(View itemView, OnItemClickListener itemClickListener) {
super(itemView);
this.mItemClickListener = itemClickListener;
itemView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (mItemClickListener != null && v == itemView) {
mItemClickListener.onItemClick(v, 0);
}
}
}
private static class ImageHolder extends MediaViewHolder implements View.OnClickListener {
private final boolean hasCamera;
private final OnItemClickListener mItemClickListener;
private final OnCheckedClickListener mCheckedClickListener;
private ImageView mIvImage;
private CheckBox mCheckBox;
private FrameLayout mLayoutLayer;
private AlbumFile mAlbumFile;
private Context mContext;
ImageHolder(Context context, View itemView, boolean hasCamera,
OnItemClickListener itemClickListener, OnCheckedClickListener checkedClickListener) {
super(itemView);
this.mContext = context;
this.hasCamera = hasCamera;
this.mItemClickListener = itemClickListener;
this.mCheckedClickListener = checkedClickListener;
mIvImage = itemView.findViewById(R.id.iv_album_content_image);
mCheckBox = itemView.findViewById(R.id.check_box);
mLayoutLayer = itemView.findViewById(R.id.layout_layer);
itemView.setOnClickListener(this);
mCheckBox.setOnClickListener(this);
mLayoutLayer.setOnClickListener(this);
}
@Override
public void setData(AlbumFile albumFile) {
mAlbumFile = albumFile;
if (albumFile.isDisable()) {
mCheckBox.setBackgroundResource(R.drawable.sobot_photo_unabled);
} else {
mCheckBox.setBackgroundResource(R.drawable.sobot_bg_checkbox);
mCheckBox.setChecked(albumFile.isChecked());
}
SobotAlbum.getAlbumConfig()
.getAlbumLoader()
.load(mIvImage, albumFile);
// mLayoutLayer.setVisibility(albumFile.isDisable() ? View.VISIBLE : View.GONE);
}
@Override
public void onClick(View v) {
if (v == itemView) {
int camera = hasCamera ? 1 : 0;
mItemClickListener.onItemClick(v, getAdapterPosition() - camera);
} else if (v == mCheckBox) {
int camera = hasCamera ? 1 : 0;
if (mAlbumFile != null && mAlbumFile.getSize() >= 50 * 1024 * 1024) {
mCheckedClickListener.onCheckedFail();
} else {
mCheckedClickListener.onCheckedClick(mCheckBox, getAdapterPosition() - camera);
}
} else if (v == mLayoutLayer) {
int camera = hasCamera ? 1 : 0;
mItemClickListener.onItemClick(v, getAdapterPosition() - camera);
}
}
}
private static class VideoHolder extends MediaViewHolder implements View.OnClickListener {
private final boolean hasCamera;
private final OnItemClickListener mItemClickListener;
private final OnCheckedClickListener mCheckedClickListener;
private ImageView mIvImage;
private CheckBox mCheckBox;
private TextView mTvDuration;
private FrameLayout mLayoutLayer;
private AlbumFile mAlbumFile;
private Context mContext;
VideoHolder(Context context, View itemView, boolean hasCamera,
OnItemClickListener itemClickListener, OnCheckedClickListener checkedClickListener) {
super(itemView);
this.mContext = context;
this.hasCamera = hasCamera;
this.mItemClickListener = itemClickListener;
this.mCheckedClickListener = checkedClickListener;
mIvImage = itemView.findViewById(R.id.iv_album_content_image);
mCheckBox = itemView.findViewById(R.id.check_box);
mTvDuration = itemView.findViewById(R.id.tv_duration);
mLayoutLayer = itemView.findViewById(R.id.layout_layer);
itemView.setOnClickListener(this);
mCheckBox.setOnClickListener(this);
mLayoutLayer.setOnClickListener(this);
}
@Override
public void setData(AlbumFile albumFile) {
mAlbumFile = albumFile;
SobotAlbum.getAlbumConfig().getAlbumLoader().load(mIvImage, albumFile);
if (albumFile.isDisable()) {
mCheckBox.setBackgroundResource(R.drawable.sobot_photo_unabled);
} else {
mCheckBox.setBackgroundResource(R.drawable.sobot_bg_checkbox);
mCheckBox.setChecked(albumFile.isChecked());
}
mTvDuration.setText(AlbumUtils.convertDuration(albumFile.getDuration()));
// mLayoutLayer.setVisibility(albumFile.isDisable() ? View.VISIBLE : View.GONE);
}
@Override
public void onClick(View v) {
if (v == itemView) {
int camera = hasCamera ? 1 : 0;
mItemClickListener.onItemClick(v, getAdapterPosition() - camera);
} else if (v == mCheckBox) {
int camera = hasCamera ? 1 : 0;
if (mAlbumFile != null && mAlbumFile.getSize() >= 50 * 1024 * 1024) {
mCheckedClickListener.onCheckedFail();
} else {
mCheckedClickListener.onCheckedClick(mCheckBox, getAdapterPosition() - camera);
}
} else if (v == mLayoutLayer) {
if (mItemClickListener != null) {
int camera = hasCamera ? 1 : 0;
mItemClickListener.onItemClick(v, getAdapterPosition() - camera);
}
}
}
}
private abstract static class MediaViewHolder extends RecyclerView.ViewHolder {
public MediaViewHolder(View itemView) {
super(itemView);
}
/**
* Bind Item data.
*/
public abstract void setData(AlbumFile albumFile);
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.app.album;
import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.Color;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.AlbumFolder;
import com.sobot.album.R;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.impl.OnCheckedClickListener;
import com.sobot.album.impl.OnItemClickListener;
import com.sobot.album.widget.divider.Api21ItemDivider;
class AlbumView extends Contract.AlbumView implements View.OnClickListener {
private Activity mActivity;
private RecyclerView mRecyclerView;
private GridLayoutManager mLayoutManager;
private AlbumAdapter mAdapter;
private TextView mBtnPreview;//预览
// 不能更改
// private TextView mBtnSwitchFolder;//更改路径
private TextView mTvOK;//确定
private TextView mTVSelect;//已选中
private LinearLayout mLayoutLoading;
private int selectCount;
private String selectText,sizeText;
public AlbumView(Activity activity, Contract.AlbumPresenter presenter) {
super(activity, presenter);
this.mActivity = activity;
selectText = mActivity.getResources().getString(R.string.sobot_select_photo_num);
sizeText = mActivity.getResources().getString(R.string.sobot_pic_siza_xiaoyu);
this.mRecyclerView = activity.findViewById(R.id.recycler_view);
// this.mBtnSwitchFolder = activity.findViewById(R.id.sobot_text_title);
this.mBtnPreview = activity.findViewById(R.id.sobot_tv_preview);
this.mTvOK = activity.findViewById(R.id.sobot_tv_add);
this.mTVSelect = activity.findViewById(R.id.sobot_tv_select);
this.mLayoutLoading = activity.findViewById(R.id.layout_loading);
// this.mBtnSwitchFolder.setOnClickListener(this);
this.mBtnPreview.setOnClickListener(this);
this.mTvOK.setOnClickListener(this);
this.mTVSelect.setOnClickListener(this);
}
@Override
protected void onCreateOptionsMenu(Menu menu) {
// getMenuInflater().inflate(R.menu.album_menu_album, menu);
// mCompleteMenu = menu.findItem(R.id.album_menu_finish);
}
@Override
protected void onOptionsItemSelected(MenuItem item) {
// int itemId = item.getItemId();
// if (itemId == R.id.album_menu_finish) {
// getPresenter().complete();
// }
}
@Override
public void setupViews(Widget widget, int column, boolean hasCamera, int choiceMode) {
/* SystemBar.setNavigationBarColor(mActivity, widget.getNavigationBarColor());
int statusBarColor = widget.getStatusBarColor();
if (widget.getUiStyle() == Widget.STYLE_LIGHT) {
if (SystemBar.setStatusBarDarkFont(mActivity, true)) {
SystemBar.setStatusBarColor(mActivity, statusBarColor);
} else {
SystemBar.setStatusBarColor(mActivity, getColor(R.color.albumColorPrimaryBlack));
}
mProgressBar.setColorFilter(getColor(R.color.albumLoadingDark));
Drawable navigationIcon = getDrawable(R.drawable.album_ic_back_white);
AlbumUtils.setDrawableTint(navigationIcon, getColor(R.color.albumIconDark));
setHomeAsUpIndicator(navigationIcon);
Drawable completeIcon = mCompleteMenu.getIcon();
AlbumUtils.setDrawableTint(completeIcon, getColor(R.color.albumIconDark));
mCompleteMenu.setIcon(completeIcon);
} else {
mProgressBar.setColorFilter(widget.getToolBarColor());
SystemBar.setStatusBarColor(mActivity, statusBarColor);
setHomeAsUpIndicator(R.drawable.album_ic_back_white);
}
mToolbar.setBackgroundColor(widget.getToolBarColor());
*/
Configuration config = mActivity.getResources().getConfiguration();
mLayoutManager = new GridLayoutManager(getContext(), column, getOrientation(config), false);
mRecyclerView.setLayoutManager(mLayoutManager);
int dividerSize = getResources().getDimensionPixelSize(R.dimen.album_dp_4);
mRecyclerView.addItemDecoration(new Api21ItemDivider(Color.TRANSPARENT, dividerSize, dividerSize));
mAdapter = new AlbumAdapter(getContext(), hasCamera, choiceMode, widget.getMediaItemCheckSelector());
mAdapter.setAddClickListener(new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
getPresenter().clickCamera(view);
}
});
mAdapter.setCheckedClickListener(new OnCheckedClickListener() {
@Override
public void onCheckedClick(CompoundButton button, int position) {
getPresenter().tryCheckItem(button, position);
}
@Override
public void onCheckedFail() {
Toast toast = Toast.makeText(mActivity, sizeText, Toast.LENGTH_LONG);
// 可以控制toast显示的位置
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 10);
toast.show();
}
});
mAdapter.setItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
getPresenter().tryPreviewItem(position);
}
});
mRecyclerView.setAdapter(mAdapter);
}
@Override
public void setLoadingDisplay(boolean display) {
mLayoutLoading.setVisibility(display ? View.VISIBLE : View.GONE);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
int position = mLayoutManager.findFirstVisibleItemPosition();
mLayoutManager.setOrientation(getOrientation(newConfig));
mRecyclerView.setAdapter(mAdapter);
mLayoutManager.scrollToPosition(position);
}
@RecyclerView.Orientation
private int getOrientation(Configuration config) {
switch (config.orientation) {
case Configuration.ORIENTATION_PORTRAIT: {
return LinearLayoutManager.VERTICAL;
}
case Configuration.ORIENTATION_LANDSCAPE: {
return LinearLayoutManager.HORIZONTAL;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
@Override
public void setCompleteDisplay(boolean display) {
// mCompleteMenu.setVisible(display);
}
@Override
public void bindAlbumFolder(AlbumFolder albumFolder) {
// mBtnSwitchFolder.setText(albumFolder.getName());
mAdapter.setAlbumFiles(albumFolder.getAlbumFiles());
mAdapter.notifyDataSetChanged();
mRecyclerView.scrollToPosition(0);
}
@Override
public void notifyInsertItem(int position) {
mAdapter.notifyItemInserted(position);
}
@Override
public void notifyItem(int position) {
mAdapter.notifyItemChanged(position);
}
@Override
public void setCheckedCount(int count) {
selectCount = count;
String sCount = String.valueOf(count);
String text = String.format(selectText,sCount);
SpannableStringBuilder ssp = new SpannableStringBuilder(text);
ssp.setSpan(new ForegroundColorSpan(mActivity.getResources().getColor(R.color.sobot_color)),text.indexOf(sCount),text.indexOf(sCount)+sCount.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mTVSelect.setText(ssp);
if(count == 0){
mTvOK.setBackgroundResource(R.drawable.sobot_bg_theme_unable_4dp);
}else {
mTvOK.setBackgroundResource(R.drawable.sobot_bg_theme_color_4dp);
}
}
@Override
public void onClick(View v) {
if (v == mTvOK) {
if(selectCount>0) {
getPresenter().complete();
}
// } else if (v == mBtnSwitchFolder) {
// getPresenter().clickFolderSwitch();
} else if (v == mBtnPreview) {
getPresenter().tryPreviewChecked();
}
}
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album.app.album;
import android.content.Context;
import android.content.res.ColorStateList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.widget.AppCompatRadioButton;
import androidx.recyclerview.widget.RecyclerView;
import com.sobot.album.AlbumFile;
import com.sobot.album.AlbumFolder;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.impl.OnItemClickListener;
import java.util.List;
class FolderAdapter extends RecyclerView.Adapter<FolderAdapter.FolderViewHolder> {
private LayoutInflater mInflater;
private List<AlbumFolder> mAlbumFolders;
private ColorStateList mSelector;
private Context mContext;
private OnItemClickListener mItemClickListener;
public FolderAdapter(Context context, List<AlbumFolder> mAlbumFolders, ColorStateList buttonTint) {
this.mContext = context;
this.mInflater = LayoutInflater.from(context);
this.mSelector = buttonTint;
this.mAlbumFolders = mAlbumFolders;
}
public void setItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;
}
@Override
public FolderViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new FolderViewHolder(mContext, mInflater.inflate(R.layout.album_item_dialog_folder, parent, false),
mSelector,
new OnItemClickListener() {
private int oldPosition = 0;
@Override
public void onItemClick(View view, int position) {
if (mItemClickListener != null)
mItemClickListener.onItemClick(view, position);
AlbumFolder albumFolder = mAlbumFolders.get(position);
if (!albumFolder.isChecked()) {
albumFolder.setChecked(true);
mAlbumFolders.get(oldPosition).setChecked(false);
notifyItemChanged(oldPosition);
notifyItemChanged(position);
oldPosition = position;
}
}
});
}
@Override
public void onBindViewHolder(FolderViewHolder holder, int position) {
final int newPosition = holder.getAdapterPosition();
holder.setData(mAlbumFolders.get(newPosition));
}
@Override
public int getItemCount() {
return mAlbumFolders == null ? 0 : mAlbumFolders.size();
}
static class FolderViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private OnItemClickListener mItemClickListener;
private ImageView mIvImage;
private TextView mTvTitle;
private AppCompatRadioButton mCheckBox;
private Context mContext;
private FolderViewHolder(Context context, View itemView, ColorStateList selector, OnItemClickListener itemClickListener) {
super(itemView);
this.mContext = context;
this.mItemClickListener = itemClickListener;
mIvImage = itemView.findViewById(R.id.iv_gallery_preview_image);
mTvTitle = itemView.findViewById(R.id.tv_gallery_preview_title);
mCheckBox = itemView.findViewById(R.id.rb_gallery_preview_check);
itemView.setOnClickListener(this);
}
public void setData(AlbumFolder albumFolder) {
List<AlbumFile> albumFiles = albumFolder.getAlbumFiles();
mTvTitle.setText("(" + albumFiles.size() + ") " + albumFolder.getName());
mCheckBox.setChecked(albumFolder.isChecked());
SobotAlbum.getAlbumConfig().getAlbumLoader().load(mIvImage, albumFiles.get(0));
}
@Override
public void onClick(View v) {
if (mItemClickListener != null)
mItemClickListener.onItemClick(v, getAdapterPosition());
}
}
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album.app.album;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.Window;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.sobot.album.AlbumFolder;
import com.sobot.album.R;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.impl.OnItemClickListener;
import java.util.List;
/**
* <p>Folder preview.</p>
* Created by Yan Zhenjie on 2016/10/18.
*/
public class FolderDialog extends BottomSheetDialog {
private Widget mWidget;
private FolderAdapter mFolderAdapter;
private List<AlbumFolder> mAlbumFolders;
private int mCurrentPosition = 0;
private OnItemClickListener mItemClickListener;
public FolderDialog(Context context, Widget widget, List<AlbumFolder> albumFolders, OnItemClickListener itemClickListener) {
super(context, R.style.Album_Dialog_Folder);
setContentView(R.layout.album_dialog_floder);
this.mWidget = widget;
this.mAlbumFolders = albumFolders;
this.mItemClickListener = itemClickListener;
RecyclerView recyclerView = getDelegate().findViewById(R.id.rv_content_list);
assert recyclerView != null;
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mFolderAdapter = new FolderAdapter(context, mAlbumFolders, widget.getBucketItemCheckSelector());
mFolderAdapter.setItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(final View view, final int position) {
if (mCurrentPosition != position) {
mAlbumFolders.get(mCurrentPosition).setChecked(false);
mFolderAdapter.notifyItemChanged(mCurrentPosition);
mCurrentPosition = position;
mAlbumFolders.get(mCurrentPosition).setChecked(true);
mFolderAdapter.notifyItemChanged(mCurrentPosition);
if (mItemClickListener != null) {
mItemClickListener.onItemClick(view, position);
}
}
dismiss();
}
});
recyclerView.setAdapter(mFolderAdapter);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
if (window != null) {
Display display = window.getWindowManager().getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= 17) display.getRealMetrics(metrics);
else display.getMetrics(metrics);
int minSize = Math.min(metrics.widthPixels, metrics.heightPixels);
window.setLayout(minSize, -1);
if (Build.VERSION.SDK_INT >= 21) {
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(mWidget.getNavigationBarColor());
}
}
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album;
import android.os.Bundle;
import androidx.annotation.Nullable;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.app.gallery.GalleryView;
import com.sobot.album.util.AlbumUtils;
import com.sobot.widget.ui.base.SobotBaseActivity;
import java.util.ArrayList;
/**
* <p>Preview the pictures in the folder in enlarged form.</p>
* Created by Yan Zhenjie on 2017/3/25.
* 预览
*/
public class GalleryActivity extends SobotBaseActivity implements Contract.GalleryPresenter {
public static ArrayList<AlbumFile> sAlbumFiles;
public static int sCheckedCount;
public static int sCurrentPosition;
public static Callback sCallback;
public static boolean previewMySelect;
private Widget mWidget;
private int mFunction;
private int mAllowSelectCount;
private Contract.GalleryView<AlbumFile> mView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
changeAppLanguage();
super.onCreate(savedInstanceState);
}
@Override
protected int getContentViewResId() {
return R.layout.sobot_activity_album_gallery;
}
@Override
protected void initView() throws InterruptedException {
mView = new GalleryView<>(this, getSupportFragmentManager(),this);
Bundle argument = getIntent().getExtras();
assert argument != null;
mWidget = argument.getParcelable(SobotAlbum.KEY_INPUT_WIDGET);
mFunction = argument.getInt(SobotAlbum.KEY_INPUT_FUNCTION);
mAllowSelectCount = argument.getInt(SobotAlbum.KEY_INPUT_LIMIT_COUNT);
mView.setShowTitle(previewMySelect);
mView.setupViews(mWidget, true);
mView.bindData(sAlbumFiles);
if (sCurrentPosition == 0) {
onCurrentChanged(sCurrentPosition);
} else {
mView.setCurrentItem(sCurrentPosition);
}
setCheckedCount();
}
@Override
protected void initData() {
}
private void setCheckedCount() {
//显示已选
mView.setCompleteText(String.valueOf(sCheckedCount));
}
@Override
public void clickItem(int position) {
}
@Override
public void longClickItem(int position) {
}
@Override
public void onCurrentChanged(int position) {
sCurrentPosition = position;
mView.setTitle(sCurrentPosition + 1 + " / " + sAlbumFiles.size());
AlbumFile albumFile = sAlbumFiles.get(position);
mView.setChecked(albumFile.isChecked());
mView.setLayerDisplay(albumFile.isDisable());
if (albumFile.getMediaType() == AlbumFile.TYPE_VIDEO) {
mView.setDuration(AlbumUtils.convertDuration(albumFile.getDuration()));
mView.setDurationDisplay(true);
} else {
mView.setDurationDisplay(false);
}
}
@Override
public void onCheckedChanged() {
AlbumFile albumFile = sAlbumFiles.get(sCurrentPosition);
if (albumFile.isChecked()) {
albumFile.setChecked(false);
sCallback.onPreviewChanged(albumFile);
sCheckedCount--;
} else {
if (sCheckedCount >= mAllowSelectCount) {
int messageRes;
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
messageRes = R.plurals.album_check_image_limit;
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
messageRes = R.plurals.album_check_video_limit;
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
messageRes = R.plurals.album_check_album_limit;
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
mView.toast(getResources().getQuantityString(messageRes, mAllowSelectCount, mAllowSelectCount));
mView.setChecked(false);
} else if(albumFile.getSize()>= 50 * 1024 * 1024){
mView.toast(getResources().getString(R.string.sobot_pic_siza_xiaoyu) );
mView.setChecked(false);
} else {
albumFile.setChecked(true);
sCallback.onPreviewChanged(albumFile);
sCheckedCount++;
}
}
setCheckedCount();
}
@Override
public void complete() {
if (sCheckedCount == 0) {
int messageRes;
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
messageRes = R.string.sobot_album_check_image_little;
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
messageRes = R.string.sobot_album_check_video_little;
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
messageRes = R.string.sobot_album_check_album_little;
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
mView.toast(messageRes);
} else {
sCallback.onPreviewComplete();
finish();
}
}
@Override
public void onBackPressed() {
finish();
}
@Override
public void finish() {
sAlbumFiles = null;
sCheckedCount = 0;
sCurrentPosition = 0;
sCallback = null;
super.finish();
}
@Override
public void bye() {
}
public interface Callback {
/**
* Complete the preview.
*/
void onPreviewComplete();
/**
* Check or uncheck a item.
*
* @param albumFile target item.
*/
void onPreviewChanged(AlbumFile albumFile);
}
}
\ No newline at end of file
package com.sobot.album.app.album;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.NonNull;
import com.sobot.album.Action;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.mvp.SobotAlbumBaseActivity;
public class NullActivity extends SobotAlbumBaseActivity implements Contract.NullPresenter {
private static final String KEY_OUTPUT_IMAGE_PATH = "KEY_OUTPUT_IMAGE_PATH";
public static String parsePath(Intent intent) {
return intent.getStringExtra(KEY_OUTPUT_IMAGE_PATH);
}
private Widget mWidget;
private int mQuality = 1;
private long mLimitDuration;
private long mLimitBytes;
private Contract.NullView mView;
@Override
protected int getContentViewResId() {
return R.layout.album_activity_null;
}
@Override
protected void initView() throws InterruptedException {
mView = new NullView(this, this);
Bundle argument = getIntent().getExtras();
assert argument != null;
int function = argument.getInt(SobotAlbum.KEY_INPUT_FUNCTION);
boolean hasCamera = argument.getBoolean(SobotAlbum.KEY_INPUT_ALLOW_CAMERA);
mQuality = argument.getInt(SobotAlbum.KEY_INPUT_CAMERA_QUALITY);
mLimitDuration = argument.getLong(SobotAlbum.KEY_INPUT_CAMERA_DURATION);
mLimitBytes = argument.getLong(SobotAlbum.KEY_INPUT_CAMERA_BYTES);
mWidget = argument.getParcelable(SobotAlbum.KEY_INPUT_WIDGET);
mView.setupViews(mWidget);
mView.setTitle(mWidget.getTitle());
switch (function) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
mView.setMessage(R.string.album_not_found_image);
mView.setMakeVideoDisplay(false);
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
mView.setMessage(R.string.album_not_found_video);
mView.setMakeImageDisplay(false);
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
mView.setMessage(R.string.album_not_found_album);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
if (!hasCamera) {
mView.setMakeImageDisplay(false);
mView.setMakeVideoDisplay(false);
}
}
@Override
protected void initData() {
}
@Override
public void takePicture() {
SobotAlbum.camera(this)
.image()
.onResult(mCameraAction)
.start();
}
@Override
public void takeVideo() {
SobotAlbum.camera(this)
.video()
.quality(mQuality)
.limitDuration(mLimitDuration)
.limitBytes(mLimitBytes)
.onResult(mCameraAction)
.start();
}
private Action<String> mCameraAction = new Action<String>() {
@Override
public void onAction(@NonNull String result) {
Intent intent = new Intent();
intent.putExtra(KEY_OUTPUT_IMAGE_PATH, result);
setResult(RESULT_OK, intent);
finish();
}
};
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.app.album;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.sobot.album.R;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.util.AlbumUtils;
class NullView extends Contract.NullView implements View.OnClickListener {
private Activity mActivity;
// private Toolbar mToolbar;
private TextView mTvMessage;
private Button mBtnTakeImage;
private Button mBtnTakeVideo;
public NullView(Activity activity, Contract.NullPresenter presenter) {
super(activity, presenter);
this.mActivity = activity;
// this.mToolbar = activity.findViewById(R.id.toolbar);
this.mTvMessage = activity.findViewById(R.id.tv_message);
this.mBtnTakeImage = activity.findViewById(R.id.btn_camera_image);
this.mBtnTakeVideo = activity.findViewById(R.id.btn_camera_video);
this.mBtnTakeImage.setOnClickListener(this);
this.mBtnTakeVideo.setOnClickListener(this);
}
@Override
public void setupViews(Widget widget) {
// mToolbar.setBackgroundColor(widget.getToolBarColor());
// int statusBarColor = widget.getStatusBarColor();
// Drawable navigationIcon = getDrawable(R.drawable.album_ic_back_white);
// if (widget.getUiStyle() == Widget.STYLE_LIGHT) {
// if (SystemBar.setStatusBarDarkFont(mActivity, true)) {
// SystemBar.setStatusBarColor(mActivity, statusBarColor);
// } else {
// SystemBar.setStatusBarColor(mActivity, getColor(R.color.albumColorPrimaryBlack));
// }
//
// AlbumUtils.setDrawableTint(navigationIcon, getColor(R.color.albumIconDark));
// setHomeAsUpIndicator(navigationIcon);
// } else {
// SystemBar.setStatusBarColor(mActivity, statusBarColor);
// setHomeAsUpIndicator(navigationIcon);
// }
// SystemBar.setNavigationBarColor(mActivity, widget.getNavigationBarColor());
Widget.ButtonStyle buttonStyle = widget.getButtonStyle();
// ColorStateList buttonSelector = buttonStyle.getButtonSelector();
// mBtnTakeImage.setSupportBackgroundTintList(buttonSelector);
// mBtnTakeVideo.setSupportBackgroundTintList(buttonSelector);
if (buttonStyle.getUiStyle() == Widget.STYLE_LIGHT) {
Drawable drawable = mBtnTakeImage.getCompoundDrawables()[0];
AlbumUtils.setDrawableTint(drawable, getColor(R.color.albumIconDark));
mBtnTakeImage.setCompoundDrawables(drawable, null, null, null);
drawable = mBtnTakeVideo.getCompoundDrawables()[0];
AlbumUtils.setDrawableTint(drawable, getColor(R.color.albumIconDark));
mBtnTakeVideo.setCompoundDrawables(drawable, null, null, null);
mBtnTakeImage.setTextColor(getColor(R.color.albumFontDark));
mBtnTakeVideo.setTextColor(getColor(R.color.albumFontDark));
}
}
@Override
public void setMessage(int message) {
mTvMessage.setText(message);
}
@Override
public void setMakeImageDisplay(boolean display) {
mBtnTakeImage.setVisibility(display ? View.VISIBLE : View.GONE);
}
@Override
public void setMakeVideoDisplay(boolean display) {
mBtnTakeVideo.setVisibility(display ? View.VISIBLE : View.GONE);
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_camera_image) {
getPresenter().takePicture();
} else if (id == R.id.btn_camera_video) {
getPresenter().takeVideo();
}
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album.data;
import android.os.AsyncTask;
import com.sobot.album.AlbumFile;
import com.sobot.album.AlbumFolder;
import com.sobot.album.SobotAlbum;
import java.util.ArrayList;
import java.util.List;
/**
* <p>Image scan task.</p>
* Created by Yan Zhenjie on 2017/3/28.
*/
public class MediaReadTask extends AsyncTask<Void, Void, MediaReadTask.ResultWrapper> {
public interface Callback {
/**
* Callback the results.
*
* @param albumFolders album folder list.
*/
void onScanCallback(ArrayList<AlbumFolder> albumFolders, ArrayList<AlbumFile> checkedFiles);
}
static class ResultWrapper {
private ArrayList<AlbumFolder> mAlbumFolders;
private ArrayList<AlbumFile> mAlbumFiles;
}
private int mFunction;
private List<AlbumFile> mCheckedFiles;
private MediaReader mMediaReader;
private Callback mCallback;
public MediaReadTask(int function, List<AlbumFile> checkedFiles, MediaReader mediaReader, Callback callback) {
this.mFunction = function;
this.mCheckedFiles = checkedFiles;
this.mMediaReader = mediaReader;
this.mCallback = callback;
}
@Override
protected ResultWrapper doInBackground(Void... params) {
ArrayList<AlbumFolder> albumFolders;
switch (mFunction) {
case SobotAlbum.FUNCTION_CHOICE_IMAGE: {
albumFolders = mMediaReader.getAllImage();
break;
}
case SobotAlbum.FUNCTION_CHOICE_VIDEO: {
albumFolders = mMediaReader.getAllVideo();
break;
}
case SobotAlbum.FUNCTION_CHOICE_ALBUM: {
albumFolders = mMediaReader.getAllMedia();
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
ArrayList<AlbumFile> checkedFiles = new ArrayList<>();
if (mCheckedFiles != null && !mCheckedFiles.isEmpty()) {
List<AlbumFile> albumFiles = albumFolders.get(0).getAlbumFiles();
for (AlbumFile checkAlbumFile : mCheckedFiles) {
for (int i = 0; i < albumFiles.size(); i++) {
AlbumFile albumFile = albumFiles.get(i);
if (checkAlbumFile.equals(albumFile)) {
albumFile.setChecked(true);
checkedFiles.add(albumFile);
}
}
}
}
ResultWrapper wrapper = new ResultWrapper();
wrapper.mAlbumFolders = albumFolders;
wrapper.mAlbumFiles = checkedFiles;
return wrapper;
}
@Override
protected void onPostExecute(ResultWrapper wrapper) {
mCallback.onScanCallback(wrapper.mAlbumFolders, wrapper.mAlbumFiles);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album.data;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.FileUtils;
import android.provider.MediaStore;
import android.webkit.MimeTypeMap;
import androidx.annotation.WorkerThread;
import com.sobot.album.AlbumFile;
import com.sobot.album.AlbumFolder;
import com.sobot.album.Filter;
import com.sobot.album.R;
import com.sobot.utils.SobotLogUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class MediaReader {
private Context mContext;
private Filter<Long> mSizeFilter;
private Filter<String> mMimeFilter;
private Filter<Long> mDurationFilter;
private boolean mFilterVisibility;
public MediaReader(Context context, Filter<Long> sizeFilter, Filter<String> mimeFilter, Filter<Long> durationFilter, boolean filterVisibility) {
this.mContext = context;
this.mSizeFilter = sizeFilter;
this.mMimeFilter = mimeFilter;
this.mDurationFilter = durationFilter;
this.mFilterVisibility = filterVisibility;
}
/**
* Image attribute.
*/
private static final String[] IMAGES = {
MediaStore.Images.Media.DATA,
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
MediaStore.Images.Media.MIME_TYPE,
MediaStore.Images.Media.DATE_ADDED,
MediaStore.Images.Media.LATITUDE,
MediaStore.Images.Media.LONGITUDE,
MediaStore.Images.Media.SIZE,
MediaStore.Images.Media._ID
};
/**
* Scan for image files.
*/
@WorkerThread
private void scanImageFile(Map<String, AlbumFolder> albumFolderMap, AlbumFolder allFileFolder) {
Uri collection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
}
ContentResolver contentResolver = mContext.getContentResolver();
Cursor cursor = contentResolver.query(collection,
IMAGES,
null,
null,
null);
if (cursor != null) {
if(cursor.moveToFirst()){
SobotLogUtils.d("======图片总数======="+cursor.getCount());
}
while (cursor.moveToNext()) {
String path = cursor.getString(0);
String bucketName = cursor.getString(1);
String mimeType = cursor.getString(2);
long addDate = cursor.getLong(3);
float latitude = cursor.getFloat(4);
float longitude = cursor.getFloat(5);
long size = cursor.getLong(6);
long id = cursor.getLong(7);
AlbumFile imageFile = new AlbumFile();
imageFile.setMediaType(AlbumFile.TYPE_IMAGE);
imageFile.setPath(path);
imageFile.setBucketName(bucketName);
imageFile.setMimeType(mimeType);
imageFile.setAddDate(addDate);
imageFile.setLatitude(latitude);
imageFile.setLongitude(longitude);
imageFile.setSize(size);
Uri contentUri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
imageFile.setUri(contentUri);
if (mSizeFilter != null && mSizeFilter.filter(size)) {
if (!mFilterVisibility) continue;
imageFile.setDisable(true);
}
if (mMimeFilter != null && mMimeFilter.filter(mimeType)) {
if (!mFilterVisibility) continue;
imageFile.setDisable(true);
}
allFileFolder.addAlbumFile(imageFile);
AlbumFolder albumFolder = albumFolderMap.get(bucketName);
if (albumFolder != null)
albumFolder.addAlbumFile(imageFile);
else {
albumFolder = new AlbumFolder();
albumFolder.setName(bucketName);
albumFolder.addAlbumFile(imageFile);
albumFolderMap.put(bucketName, albumFolder);
}
}
cursor.close();
}
}
/**
* Video attribute.
*/
private static final String[] VIDEOS = {
MediaStore.Video.Media.DATA,
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
MediaStore.Video.Media.MIME_TYPE,
MediaStore.Video.Media.DATE_ADDED,
MediaStore.Video.Media.LATITUDE,
MediaStore.Video.Media.LONGITUDE,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media._ID
};
/**
* Scan for image files.
*/
@WorkerThread
private void scanVideoFile(Map<String, AlbumFolder> albumFolderMap, AlbumFolder allFileFolder) {
Uri collection;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL);
} else {
collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
}
ContentResolver contentResolver = mContext.getContentResolver();
Cursor cursor = contentResolver.query(collection,
VIDEOS,
null,
null,
null);
SobotLogUtils.d("======scanVideoFile======="+(cursor==null));
if (cursor != null) {
if(cursor.moveToFirst()){
SobotLogUtils.d("======视频总数======="+cursor.getCount());
}else{
SobotLogUtils.d("======未找到任何视频======="+cursor.getCount());
}
while (cursor.moveToNext()) {
String path = cursor.getString(0);
String bucketName = cursor.getString(1);
String mimeType = cursor.getString(2);
long addDate = cursor.getLong(3);
float latitude = cursor.getFloat(4);
float longitude = cursor.getFloat(5);
long size = cursor.getLong(6);
long duration = cursor.getLong(7);
long id = cursor.getLong(8);
AlbumFile videoFile = new AlbumFile();
videoFile.setMediaType(AlbumFile.TYPE_VIDEO);
videoFile.setPath(path);
videoFile.setBucketName(bucketName);
videoFile.setMimeType(mimeType);
videoFile.setAddDate(addDate);
videoFile.setLatitude(latitude);
videoFile.setLongitude(longitude);
videoFile.setSize(size);
videoFile.setDuration(duration);
Uri contentUri = ContentUris.withAppendedId(
MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id);
videoFile.setUri(contentUri);
if (mSizeFilter != null && mSizeFilter.filter(size)) {
if (!mFilterVisibility) continue;
videoFile.setDisable(true);
}
if (mMimeFilter != null && mMimeFilter.filter(mimeType)) {
if (!mFilterVisibility) continue;
videoFile.setDisable(true);
}
if (mDurationFilter != null && mDurationFilter.filter(duration)) {
if (!mFilterVisibility) continue;
videoFile.setDisable(true);
}
allFileFolder.addAlbumFile(videoFile);
AlbumFolder albumFolder = albumFolderMap.get(bucketName);
if (albumFolder != null)
albumFolder.addAlbumFile(videoFile);
else {
albumFolder = new AlbumFolder();
albumFolder.setName(bucketName);
albumFolder.addAlbumFile(videoFile);
albumFolderMap.put(bucketName, albumFolder);
}
}
cursor.close();
}
}
public static File uriToFileApiQ(Context context, Uri uri) {
File file = null;
if (uri == null) return file;
//android10以上转换
if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
file = new File(uri.getPath());
} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
//把文件复制到沙盒目录
ContentResolver contentResolver = context.getContentResolver();
String displayName = System.currentTimeMillis() + Math.round((Math.random() + 1) * 1000)
+ "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(contentResolver.getType(uri));
try {
InputStream is = contentResolver.openInputStream(uri);
File cache = new File(context.getCacheDir().getAbsolutePath(), displayName);
FileOutputStream fos = new FileOutputStream(cache);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
FileUtils.copy(is, fos);
}
file = cache;
fos.close();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
/**
* Scan the list of pictures in the library.
*/
@WorkerThread
public ArrayList<AlbumFolder> getAllImage() {
Map<String, AlbumFolder> albumFolderMap = new HashMap<>();
AlbumFolder allFileFolder = new AlbumFolder();
allFileFolder.setChecked(true);
allFileFolder.setName(mContext.getString(R.string.sobot_album_all_images));
scanImageFile(albumFolderMap, allFileFolder);
SobotLogUtils.d("========getAllImage=====");
ArrayList<AlbumFolder> albumFolders = new ArrayList<>();
Collections.sort(allFileFolder.getAlbumFiles());
albumFolders.add(allFileFolder);
for (Map.Entry<String, AlbumFolder> folderEntry : albumFolderMap.entrySet()) {
AlbumFolder albumFolder = folderEntry.getValue();
Collections.sort(albumFolder.getAlbumFiles());
albumFolders.add(albumFolder);
}
return albumFolders;
}
/**
* Scan the list of videos in the library.
*/
@WorkerThread
public ArrayList<AlbumFolder> getAllVideo() {
Map<String, AlbumFolder> albumFolderMap = new HashMap<>();
AlbumFolder allFileFolder = new AlbumFolder();
allFileFolder.setChecked(true);
allFileFolder.setName(mContext.getString(R.string.sobot_album_all_videos));
scanVideoFile(albumFolderMap, allFileFolder);
ArrayList<AlbumFolder> albumFolders = new ArrayList<>();
Collections.sort(allFileFolder.getAlbumFiles());
albumFolders.add(allFileFolder);
for (Map.Entry<String, AlbumFolder> folderEntry : albumFolderMap.entrySet()) {
AlbumFolder albumFolder = folderEntry.getValue();
Collections.sort(albumFolder.getAlbumFiles());
albumFolders.add(albumFolder);
}
return albumFolders;
}
/**
* Get all the multimedia files, including videos and pictures.
*/
@WorkerThread
public ArrayList<AlbumFolder> getAllMedia() {
Map<String, AlbumFolder> albumFolderMap = new HashMap<>();
AlbumFolder allFileFolder = new AlbumFolder();
allFileFolder.setChecked(true);
allFileFolder.setName(mContext.getString(R.string.sobot_album_all_images_videos));
SobotLogUtils.d("========getAllMedia=====");
scanImageFile(albumFolderMap, allFileFolder);
scanVideoFile(albumFolderMap, allFileFolder);
ArrayList<AlbumFolder> albumFolders = new ArrayList<>();
Collections.sort(allFileFolder.getAlbumFiles());
albumFolders.add(allFileFolder);
for (Map.Entry<String, AlbumFolder> folderEntry : albumFolderMap.entrySet()) {
AlbumFolder albumFolder = folderEntry.getValue();
Collections.sort(albumFolder.getAlbumFiles());
albumFolders.add(albumFolder);
}
return albumFolders;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album.data;
import android.media.MediaPlayer;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import com.sobot.album.AlbumFile;
import com.sobot.album.Filter;
import com.sobot.album.util.AlbumUtils;
import java.io.File;
public class PathConversion {
private Filter<Long> mSizeFilter;
private Filter<String> mMimeFilter;
private Filter<Long> mDurationFilter;
public PathConversion(Filter<Long> sizeFilter, Filter<String> mimeFilter, Filter<Long> durationFilter) {
this.mSizeFilter = sizeFilter;
this.mMimeFilter = mimeFilter;
this.mDurationFilter = durationFilter;
}
@WorkerThread
@NonNull
public AlbumFile convert(String filePath) {
File file = new File(filePath);
AlbumFile albumFile = new AlbumFile();
albumFile.setPath(filePath);
File parentFile = file.getParentFile();
albumFile.setBucketName(parentFile.getName());
String mimeType = AlbumUtils.getMimeType(filePath);
albumFile.setMimeType(mimeType);
long nowTime = System.currentTimeMillis();
albumFile.setAddDate(nowTime);
albumFile.setSize(file.length());
int mediaType = 0;
if (!TextUtils.isEmpty(mimeType)) {
if (mimeType.contains("video"))
mediaType = AlbumFile.TYPE_VIDEO;
if (mimeType.contains("image"))
mediaType = AlbumFile.TYPE_IMAGE;
}
albumFile.setMediaType(mediaType);
if (mSizeFilter != null && mSizeFilter.filter(file.length())) {
albumFile.setDisable(true);
}
if (mMimeFilter != null && mMimeFilter.filter(mimeType)) {
albumFile.setDisable(true);
}
if (mediaType == AlbumFile.TYPE_VIDEO) {
MediaPlayer player = new MediaPlayer();
try {
player.setDataSource(filePath);
player.prepare();
albumFile.setDuration(player.getDuration());
} catch (Exception ignored) {
} finally {
player.release();
}
if (mDurationFilter != null && mDurationFilter.filter(albumFile.getDuration())) {
albumFile.setDisable(true);
}
}
return albumFile;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album.data;
import android.os.AsyncTask;
import com.sobot.album.AlbumFile;
public class PathConvertTask extends AsyncTask<String, Void, AlbumFile> {
public interface Callback {
/**
* The task begins.
*/
void onConvertStart();
/**
* Callback results.
*
* @param albumFile result.
*/
void onConvertCallback(AlbumFile albumFile);
}
private PathConversion mConversion;
private Callback mCallback;
public PathConvertTask(PathConversion conversion, Callback callback) {
this.mConversion = conversion;
this.mCallback = callback;
}
@Override
protected void onPreExecute() {
mCallback.onConvertStart();
}
@Override
protected AlbumFile doInBackground(String... params) {
return mConversion.convert(params[0]);
}
@Override
protected void onPostExecute(AlbumFile file) {
mCallback.onConvertCallback(file);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album.data;
import android.content.Context;
import android.os.AsyncTask;
import com.sobot.album.AlbumFile;
import java.util.ArrayList;
public class ThumbnailBuildTask extends AsyncTask<Void, Void, ArrayList<AlbumFile>> {
public interface Callback {
/**
* The task begins.
*/
void onThumbnailStart();
/**
* Callback results.
*
* @param albumFiles result.
*/
void onThumbnailCallback(ArrayList<AlbumFile> albumFiles);
}
private ArrayList<AlbumFile> mAlbumFiles;
private Callback mCallback;
private ThumbnailBuilder mThumbnailBuilder;
public ThumbnailBuildTask(Context context, ArrayList<AlbumFile> albumFiles, Callback callback) {
this.mAlbumFiles = albumFiles;
this.mCallback = callback;
this.mThumbnailBuilder = new ThumbnailBuilder(context);
}
@Override
protected void onPreExecute() {
mCallback.onThumbnailStart();
}
@Override
protected ArrayList<AlbumFile> doInBackground(Void... params) {
for (AlbumFile albumFile : mAlbumFiles) {
int mediaType = albumFile.getMediaType();
if (mediaType == AlbumFile.TYPE_IMAGE) {
albumFile.setThumbPath(mThumbnailBuilder.createThumbnailForImage(albumFile.getPath()));
} else if (mediaType == AlbumFile.TYPE_VIDEO) {
albumFile.setThumbPath(mThumbnailBuilder.createThumbnailForVideo(albumFile.getPath()));
}
}
return mAlbumFiles;
}
@Override
protected void onPostExecute(ArrayList<AlbumFile> albumFiles) {
mCallback.onThumbnailCallback(albumFiles);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.album.data;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.media.MediaMetadataRetriever;
import android.text.TextUtils;
import android.webkit.URLUtil;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.sobot.album.util.AlbumUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
public class ThumbnailBuilder {
private static final int THUMBNAIL_SIZE = 360;
private static final int THUMBNAIL_QUALITY = 80;
private File mCacheDir;
public ThumbnailBuilder(Context context) {
this.mCacheDir = AlbumUtils.getAlbumRootPath(context);
if (mCacheDir.exists() && mCacheDir.isFile()) mCacheDir.delete();
if (!mCacheDir.exists()) mCacheDir.mkdirs();
}
/**
* Create a thumbnail for the image.
*
* @param imagePath image path.
* @return thumbnail path.
*/
@WorkerThread
@Nullable
public String createThumbnailForImage(String imagePath) {
if (TextUtils.isEmpty(imagePath)) return null;
File inFile = new File(imagePath);
if (!inFile.exists()) return null;
File thumbnailFile = randomPath(imagePath);
if (thumbnailFile.exists()) return thumbnailFile.getAbsolutePath();
Bitmap inBitmap = readImageFromPath(imagePath, THUMBNAIL_SIZE, THUMBNAIL_SIZE);
if (inBitmap == null) return null;
ByteArrayOutputStream compressStream = new ByteArrayOutputStream();
inBitmap.compress(Bitmap.CompressFormat.JPEG, THUMBNAIL_QUALITY, compressStream);
try {
compressStream.close();
thumbnailFile.createNewFile();
FileOutputStream writeStream = new FileOutputStream(thumbnailFile);
writeStream.write(compressStream.toByteArray());
writeStream.flush();
writeStream.close();
return thumbnailFile.getAbsolutePath();
} catch (Exception ignored) {
return null;
}
}
/**
* Create a thumbnail for the video.
*
* @param videoPath video path.
* @return thumbnail path.
*/
@WorkerThread
@Nullable
public String createThumbnailForVideo(String videoPath) {
if (TextUtils.isEmpty(videoPath)) return null;
File thumbnailFile = randomPath(videoPath);
if (thumbnailFile.exists()) return thumbnailFile.getAbsolutePath();
try {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
if (URLUtil.isNetworkUrl(videoPath)) {
retriever.setDataSource(videoPath, new HashMap<String, String>());
} else {
retriever.setDataSource(videoPath);
}
Bitmap bitmap = retriever.getFrameAtTime();
thumbnailFile.createNewFile();
bitmap.compress(Bitmap.CompressFormat.JPEG, THUMBNAIL_QUALITY, new FileOutputStream(thumbnailFile));
return thumbnailFile.getAbsolutePath();
} catch (Exception ignored) {
return null;
}
}
private File randomPath(String filePath) {
String outFilePath = AlbumUtils.getMD5ForString(filePath) + ".album";
return new File(mCacheDir, outFilePath);
}
/**
* Deposit in the province read images, mViewWidth is high, the greater the picture clearer, but also the memory.
*
* @param imagePath pictures in the path of the memory card.
* @return bitmap.
*/
@Nullable
public static Bitmap readImageFromPath(String imagePath, int width, int height) {
File imageFile = new File(imagePath);
if (imageFile.exists()) {
try {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(imageFile));
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, options);
inputStream.close();
options.inJustDecodeBounds = false;
options.inSampleSize = computeSampleSize(options, width, height);
Bitmap sampledBitmap = null;
boolean attemptSuccess = false;
while (!attemptSuccess) {
inputStream = new BufferedInputStream(new FileInputStream(imageFile));
try {
sampledBitmap = BitmapFactory.decodeStream(inputStream, null, options);
attemptSuccess = true;
} catch (Exception e) {
options.inSampleSize *= 2;
}
inputStream.close();
}
String lowerPath = imagePath.toLowerCase();
if (lowerPath.endsWith(".jpg") || lowerPath.endsWith(".jpeg")) {
int degrees = computeDegree(imagePath);
if (degrees > 0) {
Matrix matrix = new Matrix();
matrix.setRotate(degrees);
Bitmap newBitmap = Bitmap.createBitmap(sampledBitmap, 0, 0, sampledBitmap.getWidth(), sampledBitmap.getHeight(), matrix, true);
if (newBitmap != sampledBitmap) {
sampledBitmap.recycle();
sampledBitmap = newBitmap;
}
}
}
return sampledBitmap;
} catch (Exception ignored) {
}
}
return null;
}
private static int computeSampleSize(BitmapFactory.Options options, int width, int height) {
int inSampleSize = 1;
if (options.outWidth > width || options.outHeight > height) {
int widthRatio = Math.round((float) options.outWidth / (float) width);
int heightRatio = Math.round((float) options.outHeight / (float) height);
inSampleSize = Math.min(widthRatio, heightRatio);
}
return inSampleSize;
}
private static int computeDegree(String path) {
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90: {
return 90;
}
case ExifInterface.ORIENTATION_ROTATE_180: {
return 180;
}
case ExifInterface.ORIENTATION_ROTATE_270: {
return 270;
}
default: {
return 0;
}
}
} catch (Exception e) {
return 0;
}
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.camera;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import com.sobot.album.Action;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.mvp.SobotAlbumBaseActivity;
import com.sobot.album.util.AlbumUtils;
import com.sobot.album.util.SystemBar;
import java.io.File;
public class CameraActivity extends SobotAlbumBaseActivity {
private static final String INSTANCE_CAMERA_FUNCTION = "INSTANCE_CAMERA_FUNCTION";
private static final String INSTANCE_CAMERA_FILE_PATH = "INSTANCE_CAMERA_FILE_PATH";
private static final String INSTANCE_CAMERA_QUALITY = "INSTANCE_CAMERA_QUALITY";
private static final String INSTANCE_CAMERA_DURATION = "INSTANCE_CAMERA_DURATION";
private static final String INSTANCE_CAMERA_BYTES = "INSTANCE_CAMERA_BYTES";
private static final int CODE_PERMISSION_IMAGE = 1;
private static final int CODE_PERMISSION_VIDEO = 2;
private static final int CODE_ACTIVITY_TAKE_IMAGE = 1;
private static final int CODE_ACTIVITY_TAKE_VIDEO = 2;
public static Action<String> sResult;
public static Action<String> sCancel;
private int mFunction;
private String mCameraFilePath;
private int mQuality;
private long mLimitDuration;
private long mLimitBytes;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SystemBar.setStatusBarColor(this, Color.TRANSPARENT);
SystemBar.setNavigationBarColor(this, Color.TRANSPARENT);
SystemBar.invasionNavigationBar(this);
SystemBar.invasionNavigationBar(this);
if (savedInstanceState != null) {
mFunction = savedInstanceState.getInt(INSTANCE_CAMERA_FUNCTION);
mCameraFilePath = savedInstanceState.getString(INSTANCE_CAMERA_FILE_PATH);
mQuality = savedInstanceState.getInt(INSTANCE_CAMERA_QUALITY);
mLimitDuration = savedInstanceState.getLong(INSTANCE_CAMERA_DURATION);
mLimitBytes = savedInstanceState.getLong(INSTANCE_CAMERA_BYTES);
} else {
Bundle bundle = getIntent().getExtras();
assert bundle != null;
mFunction = bundle.getInt(SobotAlbum.KEY_INPUT_FUNCTION);
mCameraFilePath = bundle.getString(SobotAlbum.KEY_INPUT_FILE_PATH);
mQuality = bundle.getInt(SobotAlbum.KEY_INPUT_CAMERA_QUALITY);
mLimitDuration = bundle.getLong(SobotAlbum.KEY_INPUT_CAMERA_DURATION);
mLimitBytes = bundle.getLong(SobotAlbum.KEY_INPUT_CAMERA_BYTES);
switch (mFunction) {
case SobotAlbum.FUNCTION_CAMERA_IMAGE: {
if (TextUtils.isEmpty(mCameraFilePath))
mCameraFilePath = AlbumUtils.randomJPGPath(this);
requestPermission(PERMISSION_TAKE_PICTURE, CODE_PERMISSION_IMAGE);
break;
}
case SobotAlbum.FUNCTION_CAMERA_VIDEO: {
if (TextUtils.isEmpty(mCameraFilePath))
mCameraFilePath = AlbumUtils.randomMP4Path(this);
requestPermission(PERMISSION_TAKE_VIDEO, CODE_PERMISSION_VIDEO);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
}
@Override
protected int getContentViewResId() {
return 0;
}
@Override
protected void initView() throws InterruptedException {
}
@Override
protected void initData() {
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(INSTANCE_CAMERA_FUNCTION, mFunction);
outState.putString(INSTANCE_CAMERA_FILE_PATH, mCameraFilePath);
outState.putInt(INSTANCE_CAMERA_QUALITY, mQuality);
outState.putLong(INSTANCE_CAMERA_DURATION, mLimitDuration);
outState.putLong(INSTANCE_CAMERA_BYTES, mLimitBytes);
super.onSaveInstanceState(outState);
}
@Override
protected void onPermissionGranted(int code) {
switch (code) {
case CODE_PERMISSION_IMAGE: {
AlbumUtils.takeImage(this, CODE_ACTIVITY_TAKE_IMAGE, new File(mCameraFilePath));
break;
}
case CODE_PERMISSION_VIDEO: {
AlbumUtils.takeVideo(this, CODE_ACTIVITY_TAKE_VIDEO, new File(mCameraFilePath), mQuality, mLimitDuration, mLimitBytes);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
@Override
protected void onPermissionDenied(int code) {
int messageRes;
switch (mFunction) {
case SobotAlbum.FUNCTION_CAMERA_IMAGE: {
messageRes = R.string.album_permission_camera_image_failed_hint;
break;
}
case SobotAlbum.FUNCTION_CAMERA_VIDEO: {
messageRes = R.string.album_permission_camera_video_failed_hint;
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
new AlertDialog.Builder(this)
.setCancelable(false)
.setTitle(R.string.album_title_permission_failed)
.setMessage(messageRes)
.setPositiveButton(R.string.album_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callbackCancel();
}
})
.show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case CODE_ACTIVITY_TAKE_IMAGE:
case CODE_ACTIVITY_TAKE_VIDEO: {
if (resultCode == RESULT_OK) {
callbackResult();
} else {
callbackCancel();
}
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
private void callbackResult() {
if (sResult != null) sResult.onAction(mCameraFilePath);
sResult = null;
sCancel = null;
finish();
}
private void callbackCancel() {
if (sCancel != null) sCancel.onAction("User canceled.");
sResult = null;
sCancel = null;
finish();
}
}
\ No newline at end of file
package com.sobot.album.app.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.widget.photoview.AttacherImageView;
import com.sobot.album.widget.photoview.PhotoViewAttacher;
public class ImageFragment extends Fragment implements PhotoViewAttacher.OnViewTapListener, View.OnLongClickListener {
private View mRootView;
private AttacherImageView imageView;
public AlbumFile mAlbum;
private View.OnClickListener mItemClickListener;
private View.OnClickListener mItemLongClickListener;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_img_item, container, false);
imageView = mRootView.findViewById(R.id.sobot_item_iv);
final PhotoViewAttacher attacher = new PhotoViewAttacher(imageView);
attacher.setOnViewTapListener(this);
attacher.setOnLongClickListener(this);
imageView.setAttacher(attacher);
initDate();
return mRootView;
}
public static ImageFragment getInstance(AlbumFile mAlbum) {
ImageFragment fragment = new ImageFragment();
Bundle bundle = new Bundle();
bundle.putParcelable("mAlbum", mAlbum);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void setArguments(@Nullable Bundle bundle) {
super.setArguments(bundle);
if (bundle != null) {
mAlbum = bundle.getParcelable("mAlbum");
}
}
private void initDate() {
if (mAlbum != null) {
SobotAlbum.getAlbumConfig().getAlbumLoader().load(imageView, mAlbum);
}
}
@Override
public void onViewTap(View v, float x, float y) {
if (mItemClickListener != null) {
mItemClickListener.onClick(v);
}
}
@Override
public boolean onLongClick(View v) {
if (mItemLongClickListener != null) {
mItemLongClickListener.onClick(v);
}
return true;
}
/**
* Set item click listener.
*
* @param onClickListener listener.
*/
public void setItemClickListener(View.OnClickListener onClickListener) {
this.mItemClickListener = onClickListener;
}
/**
* Set item long click listener.
*
* @param longClickListener listener.
*/
public void setItemLongClickListener(View.OnClickListener longClickListener) {
this.mItemLongClickListener = longClickListener;
}
}
package com.sobot.album.app.fragment;
import android.os.Parcelable;
import android.util.SparseArray;
import android.view.ViewGroup;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
public abstract class SobotFragmentPagerAdapter extends FragmentStatePagerAdapter {
// Sparse array to keep track of registered fragments in memory
private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public SobotFragmentPagerAdapter(FragmentManager fragmentManager) {
super(fragmentManager);
}
// Register the fragment when the item is instantiated
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
// Unregister when the item is inactive
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
// Returns the fragment for the position (if instantiated)
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
@Override
public Parcelable saveState() {
return null;
}
}
\ No newline at end of file
package com.sobot.album.app.fragment;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import java.util.List;
public class SobotViewPagerAdapter extends SobotFragmentPagerAdapter {
private List<Fragment> pagers;
private Context context;
public SobotViewPagerAdapter(Context context, FragmentManager fm,
List<Fragment> pagers) {
super(fm);
this.pagers = pagers;
this.context = context;
}
/**
* 返回每一页需要的fragment
*/
@Override
public Fragment getItem(int position) {
return pagers.get(position);
}
@Override
public int getCount() {
return pagers.size();
}
/**
* 返回每一页对应的title
*/
@Override
public CharSequence getPageTitle(int position) {
return "";
}
@Override
public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// super.destroyItem(container, position, object);
}
}
\ No newline at end of file
package com.sobot.album.app.fragment;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.MediaController;
import android.widget.VideoView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
public class VideoFragment extends Fragment {
private View mRootView;
private VideoView mVideoView;
private ImageView mIvPreview;
private ImageView mIvPlay;
private AlbumFile mAlbum;
private MediaPlayer mMediaPlayer;
private Bitmap firstFrame;
public static VideoFragment getInstance(AlbumFile mAlbum) {
VideoFragment fragment = new VideoFragment();
Bundle bundle = new Bundle();
bundle.putParcelable("mAlbum", mAlbum);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void setArguments(@Nullable Bundle bundle) {
super.setArguments(bundle);
if (bundle != null) {
mAlbum = bundle.getParcelable("mAlbum");
}
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.fragment_video_item, container, false);
initView();
return mRootView;
}
private void initView() {
mVideoView = mRootView.findViewById(R.id.fragment_preview_video);
mIvPreview = mRootView.findViewById(R.id.fragment_preview_first_frame);
mIvPlay = mRootView.findViewById(R.id.fragment_preview_play_img);
if (mAlbum != null) {
//设置预览图
// setFirstFrameDrawable(getContext(),mAlbum.getUri());
SobotAlbum.getAlbumConfig().getAlbumLoader().load(mIvPreview, mAlbum);
//设置视频
mVideoView.setVideoURI(mAlbum.getUri());
mIvPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mIvPreview.setVisibility(View.GONE);
mVideoView.setVisibility(View.VISIBLE);
mVideoView.start();
mIvPlay.setVisibility(View.GONE);
}
});
mVideoView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mVideoView.getVisibility() == View.VISIBLE) {
mVideoView.pause();
mIvPlay.setVisibility(View.VISIBLE);
} else {
mIvPreview.setVisibility(View.GONE);
mVideoView.setVisibility(View.VISIBLE);
mVideoView.start();
mIvPlay.setVisibility(View.GONE);
}
}
});
try {
Surface surface = mVideoView.getHolder().getSurface();
//surface没准备好时不执行播放
if (!surface.isValid()) {
return;
}
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
} else {
mMediaPlayer.reset();
}
mMediaPlayer.setDataSource(getContext(), mAlbum.getUri());
mMediaPlayer.setSurface(surface);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mMediaPlayer.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT);
}
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer
.OnVideoSizeChangedListener() {
@Override
public void
onVideoSizeChanged(MediaPlayer mp, int width, int height) {
updateVideoViewSize(mMediaPlayer.getVideoWidth(), mMediaPlayer
.getVideoHeight());
}
});
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
updateVideoViewSize(mp.getVideoWidth(), mp
.getVideoHeight());
// startVideo();
}
});
mMediaPlayer.setLooping(false);
mMediaPlayer.prepareAsync();
MediaController mediaController = new MediaController(getContext());
mVideoView.setMediaController(mediaController);
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mIvPlay.setVisibility(View.VISIBLE);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void setFirstFrameDrawable(Context context, final Uri url) {
new Thread(new Runnable() {
@Override
public void run() {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
mmr.setDataSource(context, url);
firstFrame = mmr.getFrameAtTime();
mHandler.sendEmptyMessage(0);
}
}).start();
}
private void updateVideoViewSize(float videoWidth, float videoHeight) {
FrameLayout.LayoutParams videoViewParam;
int height = (int) ((videoHeight / videoWidth) * mVideoView.getWidth());
videoViewParam = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, height);
videoViewParam.gravity = Gravity.CENTER;
mVideoView.setLayoutParams(videoViewParam);
}
@Override
public void onPause() {
if (mVideoView.isPlaying()) {
mVideoView.pause();
}
mIvPlay.setVisibility(View.VISIBLE);
super.onPause();
}
@Override
public void onDestroyView() {
mVideoView.suspend();
super.onDestroyView();
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mIvPreview.setVisibility(View.VISIBLE);
mIvPreview.setImageBitmap(firstFrame);
}
};
}
package com.sobot.album.app.gallery;
import android.os.Bundle;
import com.sobot.album.Action;
import com.sobot.album.ItemAction;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.mvp.SobotAlbumBaseActivity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class GalleryActivity extends SobotAlbumBaseActivity implements Contract.GalleryPresenter {
public static Action<ArrayList<String>> sResult;
public static Action<String> sCancel;
public static ItemAction<String> sClick;
public static ItemAction<String> sLongClick;
private Widget mWidget;
private ArrayList<String> mPathList;
private int mCurrentPosition;
private boolean mCheckable;
private Map<String, Boolean> mCheckedMap;
private Contract.GalleryView<String> mView;
@Override
protected int getContentViewResId() {
return R.layout.sobot_activity_album_gallery;
}
@Override
protected void initView() throws InterruptedException {
mView = new GalleryView<>(this, getSupportFragmentManager(), this);
Bundle argument = getIntent().getExtras();
assert argument != null;
mWidget = argument.getParcelable(SobotAlbum.KEY_INPUT_WIDGET);
mPathList = argument.getStringArrayList(SobotAlbum.KEY_INPUT_CHECKED_LIST);
mCurrentPosition = argument.getInt(SobotAlbum.KEY_INPUT_CURRENT_POSITION);
mCheckable = argument.getBoolean(SobotAlbum.KEY_INPUT_GALLERY_CHECKABLE);
mCheckedMap = new HashMap<>();
for (String path : mPathList) mCheckedMap.put(path, true);
mView.setTitle(mWidget.getTitle());
mView.setupViews(mWidget, mCheckable);
if (!mCheckable) mView.setBottomDisplay(false);
mView.setLayerDisplay(false);
mView.setDurationDisplay(false);
mView.bindData(mPathList);
if (mCurrentPosition == 0) {
onCurrentChanged(mCurrentPosition);
} else {
mView.setCurrentItem(mCurrentPosition);
}
setCheckedCount();
}
@Override
protected void initData() {
}
private void setCheckedCount() {
int checkedCount = 0;
for (Map.Entry<String, Boolean> entry : mCheckedMap.entrySet()) {
if (entry.getValue()) checkedCount += 1;
}
// String completeText = getString(R.string.sobot_album_menu_finish);
// completeText += "(" + checkedCount + " / " + mPathList.size() + ")";
// mView.setCompleteText(completeText);
mView.setCompleteText(String.valueOf(checkedCount));
}
@Override
public void clickItem(int position) {
if (sClick != null) {
sClick.onAction(GalleryActivity.this, mPathList.get(mCurrentPosition));
}
}
@Override
public void longClickItem(int position) {
if (sLongClick != null) {
sLongClick.onAction(GalleryActivity.this, mPathList.get(mCurrentPosition));
}
}
@Override
public void onCurrentChanged(int position) {
mCurrentPosition = position;
mView.setSubTitle(position + 1 + " / " + mPathList.size());
if (mCheckable) mView.setChecked(mCheckedMap.get(mPathList.get(position)));
}
@Override
public void onCheckedChanged() {
String path = mPathList.get(mCurrentPosition);
mCheckedMap.put(path, !mCheckedMap.get(path));
setCheckedCount();
}
@Override
public void complete() {
if (sResult != null) {
ArrayList<String> checkedList = new ArrayList<>();
for (Map.Entry<String, Boolean> entry : mCheckedMap.entrySet()) {
if (entry.getValue()) checkedList.add(entry.getKey());
}
sResult.onAction(checkedList);
}
finish();
}
@Override
public void onBackPressed() {
if (sCancel != null) sCancel.onAction("User canceled.");
finish();
}
@Override
public void finish() {
sResult = null;
sCancel = null;
sClick = null;
sLongClick = null;
super.finish();
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.app.gallery;
import android.os.Bundle;
import com.sobot.album.Action;
import com.sobot.album.AlbumFile;
import com.sobot.album.ItemAction;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.mvp.SobotAlbumBaseActivity;
import com.sobot.album.util.AlbumUtils;
import java.util.ArrayList;
public class GalleryAlbumActivity extends SobotAlbumBaseActivity implements Contract.GalleryPresenter {
public static Action<ArrayList<AlbumFile>> sResult;
public static Action<String> sCancel;
public static ItemAction<AlbumFile> sClick;
public static ItemAction<AlbumFile> sLongClick;
private Widget mWidget;
private ArrayList<AlbumFile> mAlbumFiles;
private int mCurrentPosition;
private boolean mCheckable;
private Contract.GalleryView<AlbumFile> mView;
@Override
protected int getContentViewResId() {
return R.layout.sobot_activity_album_gallery;
}
@Override
protected void initView() throws InterruptedException {
mView = new GalleryView<>(this, getSupportFragmentManager(), this);
Bundle argument = getIntent().getExtras();
assert argument != null;
mWidget = argument.getParcelable(SobotAlbum.KEY_INPUT_WIDGET);
mAlbumFiles = argument.getParcelableArrayList(SobotAlbum.KEY_INPUT_CHECKED_LIST);
mCurrentPosition = argument.getInt(SobotAlbum.KEY_INPUT_CURRENT_POSITION);
mCheckable = argument.getBoolean(SobotAlbum.KEY_INPUT_GALLERY_CHECKABLE);
mView.setTitle(mWidget.getTitle());
mView.setupViews(mWidget, mCheckable);
mView.bindData(mAlbumFiles);
if (mCurrentPosition == 0) {
onCurrentChanged(mCurrentPosition);
} else {
mView.setCurrentItem(mCurrentPosition);
}
setCheckedCount();
}
@Override
protected void initData() {
}
private void setCheckedCount() {
int checkedCount = 0;
for (AlbumFile albumFile : mAlbumFiles) {
if (albumFile.isChecked()) checkedCount += 1;
}
// String completeText = getString(R.string.sobot_album_menu_finish);
// completeText += "(" + checkedCount + " / " + mAlbumFiles.size() + ")";
// mView.setCompleteText(completeText);
mView.setCompleteText(String.valueOf(checkedCount));
}
@Override
public void clickItem(int position) {
if (sClick != null) {
sClick.onAction(GalleryAlbumActivity.this, mAlbumFiles.get(mCurrentPosition));
}
}
@Override
public void longClickItem(int position) {
if (sLongClick != null) {
sLongClick.onAction(GalleryAlbumActivity.this, mAlbumFiles.get(mCurrentPosition));
}
}
@Override
public void onCurrentChanged(int position) {
mCurrentPosition = position;
mView.setSubTitle(position + 1 + " / " + mAlbumFiles.size());
AlbumFile albumFile = mAlbumFiles.get(position);
if (mCheckable) mView.setChecked(albumFile.isChecked());
mView.setLayerDisplay(albumFile.isDisable());
if (albumFile.getMediaType() == AlbumFile.TYPE_VIDEO) {
if (!mCheckable) mView.setBottomDisplay(true);
mView.setDuration(AlbumUtils.convertDuration(albumFile.getDuration()));
mView.setDurationDisplay(true);
} else {
if (!mCheckable) mView.setBottomDisplay(false);
mView.setDurationDisplay(false);
}
}
@Override
public void onCheckedChanged() {
AlbumFile albumFile = mAlbumFiles.get(mCurrentPosition);
albumFile.setChecked(!albumFile.isChecked());
setCheckedCount();
}
@Override
public void complete() {
if (sResult != null) {
ArrayList<AlbumFile> checkedList = new ArrayList<>();
for (AlbumFile albumFile : mAlbumFiles) {
if (albumFile.isChecked()) checkedList.add(albumFile);
}
sResult.onAction(checkedList);
}
finish();
}
@Override
public void onBackPressed() {
if (sCancel != null) sCancel.onAction("User canceled.");
finish();
}
@Override
public void finish() {
sResult = null;
sCancel = null;
sClick = null;
sLongClick = null;
super.finish();
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.app.gallery;
import android.app.Activity;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager.widget.ViewPager;
import com.sobot.album.AlbumFile;
import com.sobot.album.R;
import com.sobot.album.SobotAlbum;
import com.sobot.album.api.widget.Widget;
import com.sobot.album.app.Contract;
import com.sobot.album.app.fragment.ImageFragment;
import com.sobot.album.app.fragment.SobotViewPagerAdapter;
import com.sobot.album.app.fragment.VideoFragment;
import java.util.ArrayList;
import java.util.List;
public class GalleryView<Data> extends Contract.GalleryView<Data> implements View.OnClickListener {
private Activity mActivity;
// private MenuItem mCompleteMenu;
private ViewPager mViewPager;
private LinearLayout mLayoutBottom;
private TextView mTvDuration;
private TextView sobot_tv_select;//已选多少张
private TextView sobot_tv_add;//添加到
private TextView sobot_tv_title;//预览已选时显示预览进度
private CheckBox mCheckBox;
private FrameLayout mLayoutLayer;
private int listSize;//显示的数量
private boolean showTitle = false;//是否显示头部
private int selectCount;//选中的数量
private HorizontalScrollView scrollView;//横向滑动
private LinearLayout sobot_thumbnail_ll;//横向滑动
private SobotViewPagerAdapter pagerAdapter;
private List<Fragment> fragments;
private FragmentManager fragmentManager;
public GalleryView(Activity activity, FragmentManager fragmentManager, Contract.GalleryPresenter presenter) {
super(activity, presenter);
this.mActivity = activity;
this.fragmentManager = fragmentManager;
this.mViewPager = activity.findViewById(R.id.view_pager);
this.mLayoutBottom = activity.findViewById(R.id.layout_bottom);
this.mTvDuration = activity.findViewById(R.id.tv_duration);
this.mCheckBox = activity.findViewById(R.id.check_box);
this.mLayoutLayer = activity.findViewById(R.id.layout_layer);
this.sobot_tv_select = activity.findViewById(R.id.sobot_tv_select);
this.sobot_tv_add = activity.findViewById(R.id.sobot_tv_add);
this.sobot_tv_title = activity.findViewById(R.id.sobot_text_title_center);
this.scrollView = activity.findViewById(R.id.sobot_hscroll_view);
this.sobot_thumbnail_ll = activity.findViewById(R.id.sobot_thumbnail_ll);
this.mCheckBox.setOnClickListener(this);
this.sobot_tv_add.setOnClickListener(this);
this.mLayoutLayer.setOnClickListener(this);
}
public void setShowTitle(boolean showTitle) {
this.showTitle = showTitle;
}
@Override
protected void onCreateOptionsMenu(Menu menu) {
// getMenuInflater().inflate(R.menu.album_menu_gallery, menu);
// mCompleteMenu = menu.findItem(R.id.album_menu_finish);
}
@Override
protected void onOptionsItemSelected(MenuItem item) {
// int id = item.getItemId();
// if (id == R.id.album_menu_finish) {
// getPresenter().complete();
// }
}
@Override
public void setupViews(Widget widget, boolean checkable) {
// SystemBar.invasionStatusBar(mActivity);
// SystemBar.invasionNavigationBar(mActivity);
// SystemBar.setStatusBarColor(mActivity, Color.TRANSPARENT);
// SystemBar.setNavigationBarColor(mActivity, getColor(R.color.albumSheetBottom));
// setHomeAsUpIndicator(R.drawable.album_ic_back_white);
// if (!checkable) {
// mCompleteMenu.setVisible(false);
// mCheckBox.setVisibility(View.GONE);
// } else {
// ColorStateList itemSelector = widget.getMediaItemCheckSelector();
// mCheckBox.setSupportButtonTintList(itemSelector);
// mCheckBox.setTextColor(itemSelector);
// }
mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
getPresenter().onCurrentChanged(position);
if (showTitle) {
String title = (position + 1) + "/" + listSize;
sobot_tv_title.setText(title);
sobot_tv_title.setVisibility(View.VISIBLE);
}
}
});
}
@Override
public void bindData(List<Data> dataList) {
listSize = dataList.size();
if (showTitle) {
String title = "1/" + listSize;
sobot_tv_title.setText(title);
sobot_tv_title.setVisibility(View.VISIBLE);
for (int i = 0; i < dataList.size(); i++) {
if (dataList.get(i) instanceof AlbumFile) {
AlbumFile albumFile = (AlbumFile) dataList.get(i);
View view = View.inflate(getContext(), R.layout.item_thumbnail_h, null);
ImageView thumbnail = view.findViewById(R.id.sobot_iv_album_content_image);
ImageView play = view.findViewById(R.id.sobot_iv_play_image);
SobotAlbum.getAlbumConfig().
getAlbumLoader().
load(thumbnail, albumFile);
int finalI = i;
thumbnail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setCurrentItem(finalI);
}
});
if (albumFile.getMediaType() == AlbumFile.TYPE_VIDEO) {
play.setVisibility(View.VISIBLE);
} else {
play.setVisibility(View.GONE);
}
sobot_thumbnail_ll.addView(view);
}
}
scrollView.setVisibility(View.VISIBLE);
}
/*PreviewAdapter<Data> adapter = new PreviewAdapter<Data>(getContext(), dataList) {
@Override
protected void loadPreview(ImageView imageView, Data item, int position) {
if (item instanceof String) {
Album.getAlbumConfig().getAlbumLoader().load(imageView, (String)item);
} else if (item instanceof AlbumFile) {
Album.getAlbumConfig().getAlbumLoader().load(imageView, (AlbumFile)item);
}
}
};
adapter.setItemClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getPresenter().clickItem(mViewPager.getCurrentItem());
}
});
adapter.setItemLongClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getPresenter().longClickItem(mViewPager.getCurrentItem());
}
});
if (adapter.getCount() > 3) {
mViewPager.setOffscreenPageLimit(3);
} else if (adapter.getCount() > 2) {
mViewPager.setOffscreenPageLimit(2);
}
mViewPager.setAdapter(adapter);*/
fragments = new ArrayList<>();
for (int i = 0; i < dataList.size(); i++) {
if (dataList.get(i) instanceof AlbumFile) {
AlbumFile albumFile = (AlbumFile) dataList.get(i);
if (albumFile.getMediaType() == AlbumFile.TYPE_IMAGE) {
ImageFragment fragment = ImageFragment.getInstance(albumFile);
fragments.add(fragment);
} else if (albumFile.getMediaType() == AlbumFile.TYPE_VIDEO) {
VideoFragment fragment = VideoFragment.getInstance(albumFile);
fragments.add(fragment);
}
}
}
pagerAdapter = new SobotViewPagerAdapter(mActivity, fragmentManager, fragments);
mViewPager.setAdapter(pagerAdapter);
}
@Override
public void setCurrentItem(int position) {
mViewPager.setCurrentItem(position);
}
@Override
public void setDurationDisplay(boolean display) {
mTvDuration.setVisibility(display ? View.VISIBLE : View.GONE);
}
@Override
public void setDuration(String duration) {
mTvDuration.setText(duration);
}
@Override
public void setChecked(boolean checked) {
mCheckBox.setChecked(checked);
}
@Override
public void setBottomDisplay(boolean display) {
mLayoutBottom.setVisibility(display ? View.VISIBLE : View.GONE);
}
@Override
public void setLayerDisplay(boolean display) {
// mLayoutLayer.setVisibility(display ? View.VISIBLE : View.GONE);
}
@Override
public void setCompleteText(String sCheckedCount) {
String text = String.format(mActivity.getResources().getString(R.string.sobot_select_photo_num), sCheckedCount);
SpannableStringBuilder ssp = new SpannableStringBuilder(text);
ssp.setSpan(new ForegroundColorSpan(mActivity.getResources().getColor(R.color.sobot_color)), text.indexOf(sCheckedCount), text.indexOf(sCheckedCount) + sCheckedCount.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
sobot_tv_select.setText(ssp);
try {
selectCount = Integer.parseInt(sCheckedCount);
} catch (Exception e) {
selectCount = 0;
}
if (selectCount == 0) {
sobot_tv_add.setBackgroundResource(R.drawable.sobot_bg_theme_unable_4dp);
} else {
sobot_tv_add.setBackgroundResource(R.drawable.sobot_bg_theme_color_4dp);
}
}
@Override
public void onClick(View v) {
if (v == mCheckBox) {
getPresenter().onCheckedChanged();
} else if (v == mLayoutLayer) {
// Intercept click events.
} else if (v == sobot_tv_add) {
if (selectCount > 0) {
getPresenter().complete();
}
}
}
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album.app.gallery;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import com.sobot.album.widget.photoview.AttacherImageView;
import com.sobot.album.widget.photoview.PhotoViewAttacher;
import java.util.List;
/**
* <p>Adapter of preview the big picture.</p>
* Created by Yan Zhenjie on 2016/10/19.
*/
public abstract class PreviewAdapter<T> extends PagerAdapter
implements PhotoViewAttacher.OnViewTapListener, View.OnLongClickListener {
private Context mContext;
private List<T> mPreviewList;
private View.OnClickListener mItemClickListener;
private View.OnClickListener mItemLongClickListener;
public PreviewAdapter(Context context, List<T> previewList) {
this.mContext = context;
this.mPreviewList = previewList;
}
/**
* Set item click listener.
*
* @param onClickListener listener.
*/
public void setItemClickListener(View.OnClickListener onClickListener) {
this.mItemClickListener = onClickListener;
}
/**
* Set item long click listener.
*
* @param longClickListener listener.
*/
public void setItemLongClickListener(View.OnClickListener longClickListener) {
this.mItemLongClickListener = longClickListener;
}
@Override
public int getCount() {
return mPreviewList == null ? 0 : mPreviewList.size();
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
AttacherImageView imageView = new AttacherImageView(mContext);
imageView.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));
loadPreview(imageView, mPreviewList.get(position), position);
container.addView(imageView);
final PhotoViewAttacher attacher = new PhotoViewAttacher(imageView);
if (mItemClickListener != null) {
attacher.setOnViewTapListener(this);
}
if (mItemLongClickListener != null) {
attacher.setOnLongClickListener(this);
}
imageView.setAttacher(attacher);
return imageView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView(((View)object));
}
@Override
public void onViewTap(View v, float x, float y) {
mItemClickListener.onClick(v);
}
@Override
public boolean onLongClick(View v) {
mItemLongClickListener.onClick(v);
return true;
}
protected abstract void loadPreview(ImageView imageView, T item, int position);
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.impl;
import android.view.View;
public class DoubleClickWrapper implements View.OnClickListener {
private final View.OnClickListener mOnClickListener;
private long mFirstTime;
public DoubleClickWrapper(View.OnClickListener onClickListener) {
this.mOnClickListener = onClickListener;
}
@Override
public void onClick(View v) {
long now = System.currentTimeMillis();
if (now - mFirstTime <= 500) {
mOnClickListener.onClick(v);
}
mFirstTime = now;
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.impl;
import android.widget.CompoundButton;
public interface OnCheckedClickListener {
/**
* Compound button is clicked.
*
* @param button view.
* @param position the position in the list.
*/
void onCheckedClick(CompoundButton button, int position);
void onCheckedFail();
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album.impl;
import android.view.View;
/**
* <p>Listens on the item's click.</p>
* Created by Yan Zhenjie on 2016/9/23.
*/
public interface OnItemClickListener {
/**
* When Item is clicked.
*
* @param view item view.
* @param position item position.
*/
void onItemClick(View view, int position);
}
\ No newline at end of file
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.mediascanner;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.webkit.MimeTypeMap;
import java.util.LinkedList;
import java.util.List;
public class MediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mMediaScanConn;
private ScannerListener mScannerListener;
private LinkedList<String[]> mLinkedList = new LinkedList<>();
private String[] mCurrentScanPaths;
private int mScanCount = 0;
/**
* Create scanner.
*
* @param context context.
*/
public MediaScanner(Context context) {
this.mMediaScanConn = new MediaScannerConnection(context.getApplicationContext(), this);
}
/**
* Create scanner.
*
* @param context context.
* @param scannerListener {@link ScannerListener}.
* @deprecated use {@link #MediaScanner(Context)} instead.
*/
@Deprecated
public MediaScanner(Context context, ScannerListener scannerListener) {
this(context);
this.mScannerListener = scannerListener;
}
/**
* Scanner is running.
*
* @return true, other wise false.
*/
public boolean isRunning() {
return mMediaScanConn.isConnected();
}
/**
* Scan file.
*
* @param filePath file absolute path.
*/
public void scan(String filePath) {
scan(new String[]{filePath});
}
/**
* Scan file list.
*
* @param filePaths file absolute path list.
*/
public void scan(List<String> filePaths) {
scan(filePaths.toArray(new String[filePaths.size()]));
}
/**
* Scan file array.
*
* @param filePaths file absolute path array.
*/
public void scan(String[] filePaths) {
if (filePaths != null && filePaths.length > 0) {
this.mLinkedList.add(filePaths);
executeOnce();
}
}
/**
* Execute scanner.
*/
private void executeOnce() {
if (!isRunning() && mLinkedList.size() > 0) {
this.mCurrentScanPaths = mLinkedList.remove(0);
this.mMediaScanConn.connect();
}
}
@Override
public void onMediaScannerConnected() {
for (String filePath : mCurrentScanPaths) {
String extension = MimeTypeMap.getFileExtensionFromUrl(filePath);
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
mMediaScanConn.scanFile(filePath, mimeType);
}
}
@Override
public void onScanCompleted(String path, Uri uri) {
if (mScannerListener != null) mScannerListener.oneComplete(path, uri);
mScanCount += 1;
if (mScanCount == mCurrentScanPaths.length) {
mMediaScanConn.disconnect();
if (mScannerListener != null) mScannerListener.allComplete(mCurrentScanPaths);
mScanCount = 0;
mCurrentScanPaths = null;
executeOnce();
}
}
}
\ No newline at end of file
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.mediascanner;
import android.net.Uri;
/**
* @deprecated not use.
*/
@Deprecated
public interface ScannerListener {
void oneComplete(String path, Uri uri);
void allComplete(String[] filePaths);
}
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.mvp;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.appcompat.view.SupportMenuInflater;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import com.sobot.album.R;
class ActivitySource extends Source<Activity> {
private View mView;
private Toolbar mActionBar;
private Drawable mActionBarIcon;
private MenuClickListener mMenuItemSelectedListener;
ActivitySource(Activity activity) {
super(activity);
mView = activity.findViewById(android.R.id.content);
}
@Override
void prepare() {
Toolbar toolbar = getHost().findViewById(R.id.toolbar);
setActionBar(toolbar);
}
@Override
void setActionBar(Toolbar actionBar) {
this.mActionBar = actionBar;
Activity activity = getHost();
if (mActionBar != null) {
setTitle(activity.getTitle());
mActionBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (mMenuItemSelectedListener != null) {
mMenuItemSelectedListener.onMenuClick(item);
}
return true;
}
});
mActionBar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mMenuItemSelectedListener != null) {
mMenuItemSelectedListener.onHomeClick();
}
}
});
mActionBarIcon = mActionBar.getNavigationIcon();
}
}
@Override
MenuInflater getMenuInflater() {
return new SupportMenuInflater(getContext());
}
@Override
Menu getMenu() {
return mActionBar == null ? null : mActionBar.getMenu();
}
@Override
void setMenuClickListener(MenuClickListener selectedListener) {
this.mMenuItemSelectedListener = selectedListener;
}
@Override
void setDisplayHomeAsUpEnabled(boolean showHome) {
if (mActionBar != null) {
if (showHome) {
mActionBar.setNavigationIcon(mActionBarIcon);
} else {
mActionBar.setNavigationIcon(null);
}
}
}
@Override
void setHomeAsUpIndicator(@DrawableRes int icon) {
setHomeAsUpIndicator(ContextCompat.getDrawable(getContext(), icon));
}
@Override
void setHomeAsUpIndicator(Drawable icon) {
this.mActionBarIcon = icon;
if (mActionBar != null) {
mActionBar.setNavigationIcon(icon);
}
}
@Override
final void setTitle(CharSequence title) {
if (mActionBar != null)
mActionBar.setTitle(title);
}
@Override
final void setTitle(@StringRes int title) {
if (mActionBar != null)
mActionBar.setTitle(title);
}
@Override
final void setSubTitle(CharSequence title) {
if (mActionBar != null)
mActionBar.setSubtitle(title);
}
@Override
final void setSubTitle(@StringRes int title) {
if (mActionBar != null)
mActionBar.setSubtitle(title);
}
@Override
Context getContext() {
return getHost();
}
@Override
View getView() {
return mView;
}
@Override
void closeInputMethod() {
Activity activity = getHost();
View focusView = activity.getCurrentFocus();
if (focusView != null) {
InputMethodManager manager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
if (manager != null) {
manager.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
}
}
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.mvp;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
public class BaseFragment extends Fragment implements Bye {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return null;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void bye() {
Activity activity = getActivity();
if (activity != null) activity.finish();
}
}
\ No newline at end of file
package com.sobot.album.mvp;
import androidx.lifecycle.LifecycleOwner;
public interface BasePresenter extends LifecycleOwner, Bye {
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.mvp;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import androidx.annotation.ArrayRes;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.GenericLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import com.sobot.album.R;
public abstract class BaseView<Presenter extends BasePresenter> {
private Source mSource;
private Presenter mPresenter;
public BaseView(Activity activity, Presenter presenter) {
this(new ActivitySource(activity), presenter);
}
public BaseView(View view, Presenter presenter) {
this(new ViewSource(view), presenter);
}
private BaseView(Source source, Presenter presenter) {
this.mSource = source;
this.mPresenter = presenter;
this.mSource.prepare();
invalidateOptionsMenu();
mSource.setMenuClickListener(new Source.MenuClickListener() {
@Override
public void onHomeClick() {
getPresenter().bye();
}
@Override
public void onMenuClick(MenuItem item) {
optionsItemSelected(item);
}
});
getPresenter().getLifecycle().addObserver(new GenericLifecycleObserver() {
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_RESUME) {
resume();
} else if (event == Lifecycle.Event.ON_PAUSE) {
pause();
} else if (event == Lifecycle.Event.ON_STOP) {
stop();
} else if (event == Lifecycle.Event.ON_DESTROY) {
destroy();
}
}
});
}
public final Presenter getPresenter() {
return mPresenter;
}
private void resume() {
onResume();
}
protected void onResume() {
}
private void pause() {
onPause();
}
protected void onPause() {
}
private void stop() {
onStop();
}
protected void onStop() {
}
private void destroy() {
closeInputMethod();
onDestroy();
}
protected void onDestroy() {
}
/**
* Set actionBar.
*/
protected final void setActionBar(Toolbar actionBar) {
mSource.setActionBar(actionBar);
invalidateOptionsMenu();
}
/**
* ReCreate menu.
*/
protected final void invalidateOptionsMenu() {
Menu menu = mSource.getMenu();
if (menu != null) {
onCreateOptionsMenu(menu);
}
}
/**
* Get menu inflater.
*/
protected final MenuInflater getMenuInflater() {
return mSource.getMenuInflater();
}
/**
* Create menu.
*/
protected void onCreateOptionsMenu(Menu menu) {
}
private void optionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
if (!onInterceptToolbarBack()) {
getPresenter().bye();
}
} else {
onOptionsItemSelected(item);
}
}
/**
* When the menu is clicked.
*/
protected void onOptionsItemSelected(MenuItem item) {
}
/**
* Intercept the return button.
*/
protected boolean onInterceptToolbarBack() {
return false;
}
protected final void openInputMethod(View view) {
view.requestFocus();
InputMethodManager manager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (manager != null) {
manager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
}
}
protected final void closeInputMethod() {
mSource.closeInputMethod();
}
protected final void setDisplayHomeAsUpEnabled(boolean showHome) {
mSource.setDisplayHomeAsUpEnabled(showHome);
}
protected final void setHomeAsUpIndicator(@DrawableRes int icon) {
mSource.setHomeAsUpIndicator(icon);
}
protected final void setHomeAsUpIndicator(Drawable icon) {
mSource.setHomeAsUpIndicator(icon);
}
public final void setTitle(String title) {
mSource.setTitle(title);
}
public final void setTitle(@StringRes int title) {
mSource.setTitle(title);
}
public final void setSubTitle(String title) {
mSource.setSubTitle(title);
}
public final void setSubTitle(@StringRes int title) {
mSource.setSubTitle(title);
}
protected Context getContext() {
return mSource.getContext();
}
protected Resources getResources() {
return getContext().getResources();
}
public final CharSequence getText(@StringRes int id) {
return getContext().getText(id);
}
public final String getString(@StringRes int id) {
return getContext().getString(id);
}
public final String getString(@StringRes int id, Object... formatArgs) {
return getContext().getString(id, formatArgs);
}
public final Drawable getDrawable(@DrawableRes int id) {
return ContextCompat.getDrawable(mSource.getContext(), id);
}
@ColorInt
public final int getColor(@ColorRes int id) {
return ContextCompat.getColor(mSource.getContext(), id);
}
public final String[] getStringArray(@ArrayRes int id) {
return getResources().getStringArray(id);
}
public final int[] getIntArray(@ArrayRes int id) {
return getResources().getIntArray(id);
}
public void showMessageDialog(@StringRes int title, @StringRes int message) {
showMessageDialog(getText(title), getText(message));
}
public void showMessageDialog(@StringRes int title, CharSequence message) {
showMessageDialog(getText(title), message);
}
public void showMessageDialog(CharSequence title, @StringRes int message) {
showMessageDialog(title, getText(message));
}
public void showMessageDialog(CharSequence title, CharSequence message) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(title)
.setMessage(message)
.setPositiveButton(R.string.album_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create();
alertDialog.show();
}
public void showConfirmDialog(@StringRes int title, @StringRes int message, OnDialogClickListener confirmClickListener) {
showConfirmDialog(getText(title), getText(message), confirmClickListener);
}
public void showConfirmDialog(@StringRes int title, CharSequence message, OnDialogClickListener confirmClickListener) {
showConfirmDialog(getText(title), message, confirmClickListener);
}
public void showConfirmDialog(CharSequence title, @StringRes int message, OnDialogClickListener confirmClickListener) {
showConfirmDialog(title, getText(message), confirmClickListener);
}
public void showConfirmDialog(CharSequence title, CharSequence message, final OnDialogClickListener confirmClickListener) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(title)
.setMessage(message)
.setNegativeButton(R.string.album_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setPositiveButton(R.string.album_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
confirmClickListener.onClick(which);
}
})
.create();
alertDialog.show();
}
public void showMessageDialog(@StringRes int title, @StringRes int message,
OnDialogClickListener cancelClickListener, OnDialogClickListener confirmClickListener) {
showMessageDialog(getText(title), getText(message), cancelClickListener, confirmClickListener);
}
public void showMessageDialog(@StringRes int title, CharSequence message,
OnDialogClickListener cancelClickListener, OnDialogClickListener confirmClickListener) {
showMessageDialog(getText(title), message, cancelClickListener, confirmClickListener);
}
public void showMessageDialog(CharSequence title, @StringRes int message,
OnDialogClickListener cancelClickListener, OnDialogClickListener confirmClickListener) {
showMessageDialog(title, getText(message), cancelClickListener, confirmClickListener);
}
public void showMessageDialog(CharSequence title, CharSequence message,
final OnDialogClickListener cancelClickListener, final OnDialogClickListener confirmClickListener) {
AlertDialog alertDialog = new AlertDialog.Builder(getContext())
.setTitle(title)
.setMessage(message)
.setNegativeButton(R.string.album_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelClickListener.onClick(which);
}
})
.setPositiveButton(R.string.album_confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
confirmClickListener.onClick(which);
}
})
.create();
alertDialog.show();
}
public interface OnDialogClickListener {
void onClick(int which);
}
public void toast(CharSequence message) {
Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
}
public void toast(@StringRes int message) {
Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
}
}
\ No newline at end of file
package com.sobot.album.mvp;
public interface Bye {
/**
* Finish.
*/
void bye();
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.mvp;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.PermissionChecker;
import com.sobot.album.SobotAlbum;
import com.sobot.album.util.AlbumUtils;
import com.sobot.widget.ui.base.SobotBaseActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
public abstract class SobotAlbumBaseActivity extends SobotBaseActivity implements Bye {
public static final String[] PERMISSION_TAKE_PICTURE = {"android.permission.CAMERA", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};
public static final String[] PERMISSION_TAKE_VIDEO = {"android.permission.CAMERA", "android.permission.RECORD_AUDIO", "android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};
public static final String[] PERMISSION_STORAGE = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Locale locale = SobotAlbum.getAlbumConfig().getLocale();
AlbumUtils.applyLanguageForContext(this, locale);
}
/**
* Request permission.
*/
protected void requestPermission(String[] permissions, int code) {
if (Build.VERSION.SDK_INT >= 23) {
List<String> deniedPermissions = getDeniedPermissions(this, permissions);
if (deniedPermissions.isEmpty()) {
onPermissionGranted(code);
} else {
permissions = new String[deniedPermissions.size()];
deniedPermissions.toArray(permissions);
ActivityCompat.requestPermissions(this, permissions, code);
}
} else {
onPermissionGranted(code);
}
}
@Override
public final void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (isGrantedResult(grantResults)) onPermissionGranted(requestCode);
else onPermissionDenied(requestCode);
}
protected void onPermissionGranted(int code) {
}
protected void onPermissionDenied(int code) {
}
@Override
public void bye() {
onBackPressed();
}
private static List<String> getDeniedPermissions(Context context, String... permissions) {
List<String> deniedList = new ArrayList<>(2);
for (String permission : permissions) {
if (PermissionChecker.checkSelfPermission(context, permission) != PermissionChecker.PERMISSION_GRANTED) {
deniedList.add(permission);
}
}
return deniedList;
}
private static boolean isGrantedResult(int... grantResults) {
for (int result : grantResults) {
if (result == PackageManager.PERMISSION_DENIED) return false;
}
return true;
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.mvp;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
abstract class Source<Host> {
private Host mHost;
public Source(Host host) {
mHost = host;
}
public Host getHost() {
return mHost;
}
abstract void prepare();
abstract void setActionBar(Toolbar actionBar);
abstract MenuInflater getMenuInflater();
abstract Menu getMenu();
abstract void setMenuClickListener(MenuClickListener selectedListener);
abstract void setDisplayHomeAsUpEnabled(boolean showHome);
abstract void setHomeAsUpIndicator(@DrawableRes int icon);
abstract void setHomeAsUpIndicator(Drawable icon);
abstract void setTitle(CharSequence title);
abstract void setTitle(@StringRes int title);
abstract void setSubTitle(CharSequence title);
abstract void setSubTitle(@StringRes int title);
abstract Context getContext();
abstract View getView();
abstract void closeInputMethod();
interface MenuClickListener {
void onHomeClick();
void onMenuClick(MenuItem item);
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.mvp;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import androidx.appcompat.view.SupportMenuInflater;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import com.sobot.album.R;
class ViewSource extends Source<View> {
private Toolbar mActionBar;
private Drawable mActionBarIcon;
private MenuClickListener mMenuItemSelectedListener;
ViewSource(View view) {
super(view);
}
@Override
void prepare() {
Toolbar toolbar = getHost().findViewById(R.id.toolbar);
setActionBar(toolbar);
}
@Override
void setActionBar(Toolbar actionBar) {
this.mActionBar = actionBar;
if (mActionBar != null) {
mActionBar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
if (mMenuItemSelectedListener != null) {
mMenuItemSelectedListener.onMenuClick(item);
}
return true;
}
});
mActionBar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mMenuItemSelectedListener != null) {
mMenuItemSelectedListener.onHomeClick();
}
}
});
mActionBarIcon = mActionBar.getNavigationIcon();
}
}
@Override
MenuInflater getMenuInflater() {
return new SupportMenuInflater(getContext());
}
@Override
Menu getMenu() {
return mActionBar == null ? null : mActionBar.getMenu();
}
@Override
void setMenuClickListener(MenuClickListener selectedListener) {
this.mMenuItemSelectedListener = selectedListener;
}
@Override
void setDisplayHomeAsUpEnabled(boolean showHome) {
if (mActionBar != null) {
if (showHome) {
mActionBar.setNavigationIcon(mActionBarIcon);
} else {
mActionBar.setNavigationIcon(null);
}
}
}
@Override
void setHomeAsUpIndicator(@DrawableRes int icon) {
setHomeAsUpIndicator(ContextCompat.getDrawable(getContext(), icon));
}
@Override
void setHomeAsUpIndicator(Drawable icon) {
this.mActionBarIcon = icon;
if (mActionBar != null)
mActionBar.setNavigationIcon(icon);
}
@Override
final void setTitle(CharSequence title) {
if (mActionBar != null)
mActionBar.setTitle(title);
}
@Override
final void setTitle(@StringRes int title) {
if (mActionBar != null)
mActionBar.setTitle(title);
}
@Override
final void setSubTitle(CharSequence title) {
if (mActionBar != null)
mActionBar.setSubtitle(title);
}
@Override
final void setSubTitle(@StringRes int title) {
if (mActionBar != null)
mActionBar.setSubtitle(title);
}
@Override
Context getContext() {
return getHost().getContext();
}
@Override
View getView() {
return getHost();
}
@Override
void closeInputMethod() {
View focusView = getView().findFocus();
if (focusView != null) {
InputMethodManager manager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (manager != null) {
manager.hideSoftInputFromWindow(focusView.getWindowToken(), 0);
}
}
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.provider;
import android.content.Context;
import androidx.core.content.FileProvider;
/**
* <p>For external access to files.</p>
* Created by Yan Zhenjie on 2017/3/31.
*/
public class CameraFileProvider extends FileProvider {
/**
* Get the provider of the external file path.
*
* @param context context.
* @return provider.
*/
public static String getProviderName(Context context) {
return context.getPackageName() + ".app.file.provider";
}
}
\ No newline at end of file
/*
* Copyright 2016 Yan Zhenjie.
*
* 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.sobot.album.util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.webkit.MimeTypeMap;
import androidx.annotation.ColorInt;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.core.graphics.drawable.DrawableCompat;
import com.sobot.album.provider.CameraFileProvider;
import com.sobot.album.widget.divider.Api20ItemDivider;
import com.sobot.album.widget.divider.Api21ItemDivider;
import com.sobot.album.widget.divider.Divider;
import java.io.File;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.UUID;
/**
* <p>Helper for album.</p>
* Created by Yan Zhenjie on 2016/10/30.
*/
public class AlbumUtils {
private static final String CACHE_DIRECTORY = "AlbumCache";
/**
* Get a writable root directory.
*
* @param context context.
* @return {@link File}.
*/
@NonNull
public static File getAlbumRootPath(Context context) {
if (sdCardIsAvailable()) {
return new File(Environment.getExternalStorageDirectory(), CACHE_DIRECTORY);
} else {
return new File(context.getFilesDir(), CACHE_DIRECTORY);
}
}
/**
* SD card is available.
*
* @return true when available, other wise is false.
*/
public static boolean sdCardIsAvailable() {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return Environment.getExternalStorageDirectory().canWrite();
} else
return false;
}
/**
* Setting {@link Locale} for {@link Context}.
*
* @param context to set the specified locale context.
* @param locale locale.
*/
@NonNull
public static Context applyLanguageForContext(@NonNull Context context, @NonNull Locale locale) {
Resources resources = context.getResources();
Configuration config = resources.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(locale);
return context.createConfigurationContext(config);
} else {
config.locale = locale;
resources.updateConfiguration(config, resources.getDisplayMetrics());
return context;
}
}
/**
* Take picture.
*
* @param activity activity.
* @param requestCode code, see {@link Activity# onActivityResult(int, int, Intent)}.
* @param outPath file path.
*/
public static void takeImage(@NonNull Activity activity, int requestCode, File outPath) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = getUri(activity, outPath);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
activity.startActivityForResult(intent, requestCode);
}
/**
* Take video.
*
* @param activity activity.
* @param requestCode code, see {@link Activity# onActivityResult(int, int, Intent)}.
* @param outPath file path.
* @param quality currently value 0 means low quality, suitable for MMS messages, and value 1 means high quality.
* @param duration specify the maximum allowed recording duration in seconds.
* @param limitBytes specify the maximum allowed size.
*/
public static void takeVideo(@NonNull Activity activity, int requestCode, File outPath,
@IntRange(from = 0, to = 1) int quality,
@IntRange(from = 1) long duration,
@IntRange(from = 1) long limitBytes) {
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
Uri uri = getUri(activity, outPath);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, quality);
intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, duration);
intent.putExtra(MediaStore.EXTRA_SIZE_LIMIT, limitBytes);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
activity.startActivityForResult(intent, requestCode);
}
/**
* Generates an externally accessed URI based on path.
*
* @param context context.
* @param outPath file path.
* @return the uri address of the file.
*/
@NonNull
public static Uri getUri(@NonNull Context context, @NonNull File outPath) {
Uri uri;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
uri = Uri.fromFile(outPath);
} else {
uri = CameraFileProvider.getUriForFile(context, CameraFileProvider.getProviderName(context), outPath);
}
return uri;
}
/**
* Generate a random jpg file path.
*
* @return file path.
*
* @deprecated use {@link #randomJPGPath(Context)} instead.
*/
@NonNull
@Deprecated
public static String randomJPGPath() {
File bucket = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
return randomJPGPath(bucket);
}
/**
* Generate a random jpg file path.
*
* @param context context.
*
* @return file path.
*/
@NonNull
public static String randomJPGPath(Context context) {
if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return randomJPGPath(context.getCacheDir());
}
return randomJPGPath();
}
/**
* Generates a random jpg file path in the specified directory.
*
* @param bucket specify the directory.
* @return file path.
*/
@NonNull
public static String randomJPGPath(File bucket) {
return randomMediaPath(bucket, ".jpg");
}
/**
* Generate a random mp4 file path.
*
* @return file path.
*
* @deprecated use {@link #randomMP4Path(Context)} instead.
*/
@NonNull
@Deprecated
public static String randomMP4Path() {
File bucket = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
return randomMP4Path(bucket);
}
/**
* Generate a random mp4 file path.
*
* @param context context.
*
* @return file path.
*/
@NonNull
public static String randomMP4Path(Context context) {
if(!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
return randomMP4Path(context.getCacheDir());
}
return randomMP4Path();
}
/**
* Generates a random mp4 file path in the specified directory.
*
* @return file path.
*/
@NonNull
public static String randomMP4Path(File bucket) {
return randomMediaPath(bucket, ".mp4");
}
/**
* Generates a random file path using the specified suffix name in the specified directory.
*
* @param bucket specify the directory.
* @param extension extension.
* @return file path.
*/
@NonNull
private static String randomMediaPath(File bucket, String extension) {
if (bucket.exists() && bucket.isFile()) bucket.delete();
if (!bucket.exists()) bucket.mkdirs();
String outFilePath = AlbumUtils.getNowDateTime("yyyyMMdd_HHmmssSSS") + "_" + getMD5ForString(UUID.randomUUID().toString()) + extension;
File file = new File(bucket, outFilePath);
return file.getAbsolutePath();
}
/**
* Format the current time in the specified format.
*
* @return the time string.
*/
@NonNull
public static String getNowDateTime(@NonNull String format) {
SimpleDateFormat formatter = new SimpleDateFormat(format, Locale.ENGLISH);
Date curDate = new Date(System.currentTimeMillis());
return formatter.format(curDate);
}
/**
* Get the mime type of the file in the url.
*
* @param url file url.
* @return mime type.
*/
public static String getMimeType(String url) {
String extension = getExtension(url);
if (!MimeTypeMap.getSingleton().hasExtension(extension)) return "";
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
return TextUtils.isEmpty(mimeType) ? "" : mimeType;
}
/**
* Get the file extension in url.
*
* @param url file url.
* @return extension.
*/
public static String getExtension(String url) {
url = TextUtils.isEmpty(url) ? "" : url.toLowerCase();
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
return TextUtils.isEmpty(extension) ? "" : extension;
}
/**
* Specifies a tint for {@code drawable}.
*
* @param drawable drawable target, mutate.
* @param color color.
*/
public static void setDrawableTint(@NonNull Drawable drawable, @ColorInt int color) {
DrawableCompat.setTint(DrawableCompat.wrap(drawable.mutate()), color);
}
/**
* Specifies a tint for {@code drawable}.
*
* @param drawable drawable target, mutate.
* @param color color.
* @return convert drawable.
*/
@NonNull
public static Drawable getTintDrawable(@NonNull Drawable drawable, @ColorInt int color) {
drawable = DrawableCompat.wrap(drawable.mutate());
DrawableCompat.setTint(drawable, color);
return drawable;
}
/**
* {@link ColorStateList}.
*
* @param normal normal color.
* @param highLight highLight color.
* @return {@link ColorStateList}.
*/
public static ColorStateList getColorStateList(@ColorInt int normal, @ColorInt int highLight) {
int[][] states = new int[6][];
states[0] = new int[]{android.R.attr.state_checked};
states[1] = new int[]{android.R.attr.state_pressed};
states[2] = new int[]{android.R.attr.state_selected};
states[3] = new int[]{};
states[4] = new int[]{};
states[5] = new int[]{};
int[] colors = new int[]{highLight, highLight, highLight, normal, normal, normal};
return new ColorStateList(states, colors);
}
/**
* Change part of the color of CharSequence.
*
* @param content content text.
* @param start start index.
* @param end end index.
* @param color color.
* @return {@code SpannableString}.
*/
@NonNull
public static SpannableString getColorText(@NonNull CharSequence content, int start, int end, @ColorInt int color) {
SpannableString stringSpan = new SpannableString(content);
stringSpan.setSpan(new ForegroundColorSpan(color), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return stringSpan;
}
/**
* Return a color-int from alpha, red, green, blue components.
*
* @param color color.
* @param alpha alpha, alpha component [0..255] of the color.
*/
@ColorInt
public static int getAlphaColor(@ColorInt int color, @IntRange(from = 0, to = 255) int alpha) {
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(alpha, red, green, blue);
}
/**
* Generate divider.
*
* @param color color.
* @return {@link Divider}.
*/
public static Divider getDivider(@ColorInt int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return new Api21ItemDivider(color);
}
return new Api20ItemDivider(color);
}
/**
* Time conversion.
*
* @param duration ms.
* @return such as: {@code 00:00:00}, {@code 00:00}.
*/
@NonNull
public static String convertDuration(@IntRange(from = 1) long duration) {
duration /= 1000;
int hour = (int) (duration / 3600);
int minute = (int) ((duration - hour * 3600) / 60);
int second = (int) (duration - hour * 3600 - minute * 60);
String hourValue = "";
String minuteValue;
String secondValue;
if (hour > 0) {
if (hour >= 10) {
hourValue = Integer.toString(hour);
} else {
hourValue = "0" + hour;
}
hourValue += ":";
}
if (minute > 0) {
if (minute >= 10) {
minuteValue = Integer.toString(minute);
} else {
minuteValue = "0" + minute;
}
} else {
minuteValue = "00";
}
minuteValue += ":";
if (second > 0) {
if (second >= 10) {
secondValue = Integer.toString(second);
} else {
secondValue = "0" + second;
}
} else {
secondValue = "00";
}
return hourValue + minuteValue + secondValue;
}
/**
* Get the MD5 value of string.
*
* @param content the target string.
* @return the MD5 value.
*/
public static String getMD5ForString(String content) {
StringBuilder md5Buffer = new StringBuilder();
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
byte[] tempBytes = digest.digest(content.getBytes());
int digital;
for (int i = 0; i < tempBytes.length; i++) {
digital = tempBytes[i];
if (digital < 0) {
digital += 256;
}
if (digital < 16) {
md5Buffer.append("0");
}
md5Buffer.append(Integer.toHexString(digital));
}
} catch (Exception ignored) {
return Integer.toString(content.hashCode());
}
return md5Buffer.toString();
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie
*
* 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.sobot.album.util;
import android.app.Activity;
import android.graphics.Color;
import android.os.Build;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.RequiresApi;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class SystemBar {
/**
* Set the status bar color.
*/
public static void setStatusBarColor(Activity activity, int statusBarColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) setStatusBarColor(activity.getWindow(), statusBarColor);
}
/**
* Set the status bar color.
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void setStatusBarColor(Window window, int statusBarColor) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(statusBarColor);
}
/**
* Set the navigation bar color.
*/
public static void setNavigationBarColor(Activity activity, int navigationBarColor) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) setNavigationBarColor(activity.getWindow(), navigationBarColor);
}
/**
* Set the navigation bar color.
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void setNavigationBarColor(Window window, int navigationBarColor) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setNavigationBarColor(navigationBarColor);
}
/**
* Set the content layout full the StatusBar, but do not hide StatusBar.
*/
public static void invasionStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) invasionStatusBar(activity.getWindow());
}
/**
* Set the content layout full the StatusBar, but do not hide StatusBar.
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void invasionStatusBar(Window window) {
View decorView = window.getDecorView();
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setStatusBarColor(Color.TRANSPARENT);
}
/**
* Set the content layout full the NavigationBar, but do not hide NavigationBar.
*/
public static void invasionNavigationBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) invasionNavigationBar(activity.getWindow());
}
/**
* Set the content layout full the NavigationBar, but do not hide NavigationBar.
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static void invasionNavigationBar(Window window) {
View decorView = window.getDecorView();
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility()
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setNavigationBarColor(Color.TRANSPARENT);
}
/**
* Set the status bar to dark.
*/
public static boolean setStatusBarDarkFont(Activity activity, boolean darkFont) {
return setStatusBarDarkFont(activity.getWindow(), darkFont);
}
/**
* Set the status bar to dark.
*/
public static boolean setStatusBarDarkFont(Window window, boolean darkFont) {
if (setMIUIStatusBarFont(window, darkFont)) {
setDefaultStatusBarFont(window, darkFont);
return true;
} else if (setMeizuStatusBarFont(window, darkFont)) {
setDefaultStatusBarFont(window, darkFont);
return true;
} else {
return setDefaultStatusBarFont(window, darkFont);
}
}
private static boolean setMeizuStatusBarFont(Window window, boolean darkFont) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (darkFont) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
return true;
} catch (Exception ignored) {
}
return false;
}
private static boolean setMIUIStatusBarFont(Window window, boolean dark) {
Class<?> clazz = window.getClass();
try {
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
} else {
extraFlagField.invoke(window, 0, darkModeFlag);
}
return true;
} catch (Exception ignored) {
}
return false;
}
private static boolean setDefaultStatusBarFont(Window window, boolean dark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
View decorView = window.getDecorView();
if (dark) {
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
return true;
}
return false;
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ProgressBar;
import androidx.annotation.ColorInt;
public class ColorProgressBar extends ProgressBar {
public ColorProgressBar(Context context) {
super(context);
}
public ColorProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ColorProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* Set the color of the Bar.
*
* @param color color.
*/
public void setColorFilter(@ColorInt int color) {
Drawable drawable = getIndeterminateDrawable();
drawable = drawable.mutate();
drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
setIndeterminateDrawable(drawable);
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.app.Dialog;
import android.content.Context;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import com.sobot.album.R;
import com.sobot.album.api.widget.Widget;
public class LoadingDialog extends Dialog {
private ColorProgressBar mProgressBar;
private TextView mTvMessage;
public LoadingDialog(@NonNull Context context) {
super(context, R.style.Album_Dialog);
setCancelable(false);
setCanceledOnTouchOutside(false);
setContentView(R.layout.album_dialog_loading);
mProgressBar = findViewById(R.id.progress_bar);
mTvMessage = findViewById(R.id.tv_message);
}
/**
* Set some properties of the view.
*
* @param widget widget.
*/
public void setupViews(Widget widget) {
if (widget.getUiStyle() == Widget.STYLE_LIGHT) {
int color = ContextCompat.getColor(getContext(), R.color.albumLoadingDark);
mProgressBar.setColorFilter(color);
} else {
mProgressBar.setColorFilter(widget.getToolBarColor());
}
}
/**
* Set the message.
*
* @param message message resource id.
*/
public void setMessage(@StringRes int message) {
mTvMessage.setText(message);
}
/**
* Set the message.
*
* @param message message.
*/
public void setMessage(String message) {
mTvMessage.setText(message);
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
public class SquareCardView extends CardView {
private Configuration mConfig;
public SquareCardView(@NonNull Context context) {
this(context, null, 0);
}
public SquareCardView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SquareCardView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mConfig = getResources().getConfiguration();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int orientation = mConfig.orientation;
switch (orientation) {
case Configuration.ORIENTATION_PORTRAIT: {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
break;
}
case Configuration.ORIENTATION_LANDSCAPE: {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class SquareFrameLayout extends FrameLayout {
private Configuration mConfig;
public SquareFrameLayout(@NonNull Context context) {
this(context, null, 0);
}
public SquareFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SquareFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mConfig = getResources().getConfiguration();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int orientation = mConfig.orientation;
switch (orientation) {
case Configuration.ORIENTATION_PORTRAIT: {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
break;
}
case Configuration.ORIENTATION_LANDSCAPE: {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
public class SquareImageView extends AppCompatImageView {
private Configuration mConfig;
public SquareImageView(Context context) {
this(context, null, 0);
}
public SquareImageView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SquareImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mConfig = getResources().getConfiguration();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int orientation = mConfig.orientation;
switch (orientation) {
case Configuration.ORIENTATION_PORTRAIT: {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
break;
}
case Configuration.ORIENTATION_LANDSCAPE: {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class SquareRelativeLayout extends RelativeLayout {
private Configuration mConfig;
public SquareRelativeLayout(@NonNull Context context) {
this(context, null, 0);
}
public SquareRelativeLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SquareRelativeLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mConfig = getResources().getConfiguration();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int orientation = mConfig.orientation;
switch (orientation) {
case Configuration.ORIENTATION_PORTRAIT: {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
break;
}
case Configuration.ORIENTATION_LANDSCAPE: {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
break;
}
default: {
throw new AssertionError("This should not be the case.");
}
}
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class TransferLayout extends FrameLayout {
public TransferLayout(@NonNull Context context) {
super(context);
}
public TransferLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs, 0);
}
public TransferLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean performClick() {
if (getChildCount() == 1) {
return getChildAt(0).performClick();
}
return super.performClick();
}
@Override
public boolean callOnClick() {
if (getChildCount() == 1) {
return getChildAt(0).performClick();
}
return super.performClick();
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie
*
* 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.sobot.album.widget.divider;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.ColorInt;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
public class Api20ItemDivider extends Divider {
private final int mWidth;
private final int mHeight;
private final Drawer mDrawer;
/**
* @param color divider line color.
*/
public Api20ItemDivider(@ColorInt int color) {
this(color, 4, 4);
}
/**
* @param color line color.
* @param width line width.
* @param height line height.
*/
public Api20ItemDivider(@ColorInt int color, int width, int height) {
this.mWidth = Math.round(width / 2F);
this.mHeight = Math.round(height / 2F);
this.mDrawer = new ColorDrawer(color, mWidth, mHeight);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof LinearLayoutManager) {
int orientation = getOrientation(layoutManager);
int position = parent.getChildLayoutPosition(view);
int spanCount = getSpanCount(layoutManager);
int childCount = layoutManager.getItemCount();
if (orientation == RecyclerView.VERTICAL) {
offsetVertical(outRect, position, spanCount, childCount);
} else {
offsetHorizontal(outRect, position, spanCount, childCount);
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
outRect.set(mWidth, mHeight, mWidth, mHeight); // |-|-
}
}
private void offsetHorizontal(Rect outRect, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstColumn && lastColumn) { // xxxx
outRect.set(0, 0, 0, 0);
} else if (firstColumn) { // xx|x
outRect.set(0, 0, mWidth, 0);
} else if (lastColumn) { // |xxx
outRect.set(mWidth, 0, 0, 0);
} else { // |x|x
outRect.set(mWidth, 0, mWidth, 0);
}
} else {
if (firstColumn && firstRaw) { // xx|-
outRect.set(0, 0, mWidth, mHeight);
} else if (firstColumn && lastRaw) { // x-|x
outRect.set(0, mHeight, mWidth, 0);
} else if (lastColumn && firstRaw) { // |xx-
outRect.set(mWidth, 0, 0, mHeight);
} else if (lastColumn && lastRaw) { // |-xx
outRect.set(mWidth, mHeight, 0, 0);
} else if (firstColumn) { // x-|-
outRect.set(0, mHeight, mWidth, mHeight);
} else if (lastColumn) { // |-x-
outRect.set(mWidth, mHeight, 0, mHeight);
} else if (firstRaw) { // |x|-
outRect.set(mWidth, 0, mWidth, mHeight);
} else if (lastRaw) { // |-|x
outRect.set(mWidth, mHeight, mWidth, 0);
} else { // |-|-
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
}
}
private void offsetVertical(Rect outRect, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastRaw) { // xxxx
outRect.set(0, 0, 0, 0);
} else if (firstRaw) { // xxx-
outRect.set(0, 0, 0, mHeight);
} else if (lastRaw) { // x-xx
outRect.set(0, mHeight, 0, 0);
} else { // x-x-
outRect.set(0, mHeight, 0, mHeight);
}
} else {
if (firstRaw && firstColumn) { // xx|-
outRect.set(0, 0, mWidth, mHeight);
} else if (firstRaw && lastColumn) { // |xx-
outRect.set(mWidth, 0, 0, mHeight);
} else if (lastRaw && firstColumn) { // x-|x
outRect.set(0, mHeight, mWidth, 0);
} else if (lastRaw && lastColumn) { // |-xx
outRect.set(mWidth, mHeight, 0, 0);
} else if (firstRaw) { // |x|-
outRect.set(mWidth, 0, mWidth, mHeight);
} else if (lastRaw) { // |-|x
outRect.set(mWidth, mHeight, mWidth, 0);
} else if (firstColumn) { // x-|-
outRect.set(0, mHeight, mWidth, mHeight);
} else if (lastColumn) { // |-x-
outRect.set(mWidth, mHeight, 0, mHeight);
} else { // |-|-
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
}
}
private int getOrientation(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof LinearLayoutManager) {
return ((LinearLayoutManager) layoutManager).getOrientation();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager) layoutManager).getOrientation();
}
return RecyclerView.VERTICAL;
}
private int getSpanCount(RecyclerView.LayoutManager layoutManager) {
if (layoutManager instanceof GridLayoutManager) {
return ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
return ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
}
return 1;
}
private boolean isFirstRaw(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
return position < columnCount;
} else {
if (columnCount == 1) return true;
return position % columnCount == 0;
}
}
private boolean isLastRaw(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) {
return position + 1 == childCount;
} else {
int lastRawItemCount = childCount % columnCount;
int rawCount = (childCount - lastRawItemCount) / columnCount + (lastRawItemCount > 0 ? 1 : 0);
int rawPositionJudge = (position + 1) % columnCount;
if (rawPositionJudge == 0) {
int positionRaw = (position + 1) / columnCount;
return rawCount == positionRaw;
} else {
int rawPosition = (position + 1 - rawPositionJudge) / columnCount + 1;
return rawCount == rawPosition;
}
}
} else {
if (columnCount == 1) return true;
return (position + 1) % columnCount == 0;
}
}
private boolean isFirstColumn(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) return true;
return position % columnCount == 0;
} else {
return position < columnCount;
}
}
private boolean isLastColumn(int orientation, int position, int columnCount, int childCount) {
if (orientation == RecyclerView.VERTICAL) {
if (columnCount == 1) return true;
return (position + 1) % columnCount == 0;
} else {
if (columnCount == 1) {
return position + 1 == childCount;
} else {
int lastRawItemCount = childCount % columnCount;
int rawCount = (childCount - lastRawItemCount) / columnCount + (lastRawItemCount > 0 ? 1 : 0);
int rawPositionJudge = (position + 1) % columnCount;
if (rawPositionJudge == 0) {
int positionRaw = (position + 1) / columnCount;
return rawCount == positionRaw;
} else {
int rawPosition = (position + 1 - rawPositionJudge) / columnCount + 1;
return rawCount == rawPosition;
}
}
}
}
@Override
public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
int orientation = getOrientation(layoutManager);
int spanCount = getSpanCount(layoutManager);
int childCount = layoutManager.getChildCount();
if (layoutManager instanceof LinearLayoutManager) {
canvas.save();
for (int i = 0; i < childCount; i++) {
View view = layoutManager.getChildAt(i);
int position = parent.getChildLayoutPosition(view);
if (orientation == RecyclerView.VERTICAL) {
drawVertical(canvas, view, position, spanCount, childCount);
} else {
drawHorizontal(canvas, view, position, spanCount, childCount);
}
}
canvas.restore();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
canvas.save();
for (int i = 0; i < childCount; i++) {
View view = layoutManager.getChildAt(i);
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
canvas.restore();
}
}
private void drawHorizontal(Canvas canvas, View view, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.HORIZONTAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastColumn) { // xxxx
// Nothing.
} else if (firstColumn) { // xx|x
mDrawer.drawRight(view, canvas);
} else if (lastColumn) { // |xxx
mDrawer.drawLeft(view, canvas);
} else { // |x|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
}
} else {
if (firstColumn && firstRaw) { // xx|-
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstColumn && lastRaw) { // x-|x
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (lastColumn && firstRaw) { // |xx-
mDrawer.drawLeft(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn && lastRaw) { // |-xx
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
} else if (firstColumn) { // x-|-
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn) { // |-x-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstRaw) { // |x|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // |-|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else { // |-|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
}
}
private void drawVertical(Canvas canvas, View view, int position, int spanCount, int childCount) {
boolean firstRaw = isFirstRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastRaw = isLastRaw(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean firstColumn = isFirstColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
boolean lastColumn = isLastColumn(RecyclerView.VERTICAL, position, spanCount, childCount);
if (spanCount == 1) {
if (firstRaw && lastRaw) { // xxxx
// Nothing.
} else if (firstRaw) { // xxx-
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // x-xx
mDrawer.drawTop(view, canvas);
} else { // x-x-
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
}
} else {
if (firstRaw && firstColumn) { // xx|-
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (firstRaw && lastColumn) { // |xx-
mDrawer.drawLeft(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw && firstColumn) { // x-|x
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (lastRaw && lastColumn) { // |-xx
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
} else if (firstRaw) { // |x|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastRaw) { // |-|x
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
} else if (firstColumn) { // x-|-
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
} else if (lastColumn) { // |-x-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawBottom(view, canvas);
} else { // |-|-
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
}
}
@Override
public int getHeight() {
return mHeight;
}
@Override
public int getWidth() {
return mWidth;
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie
*
* 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.sobot.album.widget.divider;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.ColorInt;
import androidx.recyclerview.widget.RecyclerView;
public class Api21ItemDivider extends Divider {
private final int mWidth;
private final int mHeight;
private final Drawer mDrawer;
/**
* @param color divider line color.
*/
public Api21ItemDivider(@ColorInt int color) {
this(color, 4, 4);
}
/**
* @param color line color.
* @param width line width.
* @param height line height.
*/
public Api21ItemDivider(@ColorInt int color, int width, int height) {
this.mWidth = Math.round(width / 2F);
this.mHeight = Math.round(height / 2F);
this.mDrawer = new ColorDrawer(color, mWidth, mHeight);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.set(mWidth, mHeight, mWidth, mHeight);
}
@Override
public void onDraw(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
canvas.save();
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
int childCount = layoutManager.getChildCount();
for (int i = 0; i < childCount; i++) {
final View view = layoutManager.getChildAt(i);
mDrawer.drawLeft(view, canvas);
mDrawer.drawTop(view, canvas);
mDrawer.drawRight(view, canvas);
mDrawer.drawBottom(view, canvas);
}
canvas.restore();
}
@Override
public int getHeight() {
return mHeight;
}
@Override
public int getWidth() {
return mWidth;
}
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget.divider;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import androidx.annotation.ColorInt;
class ColorDrawer extends Drawer {
public ColorDrawer(int color, int width, int height) {
super(new ColorDrawable(opaqueColor(color)), width, height);
}
/**
* The target color is packaged in an opaque color.
*
* @param color color.
* @return color.
*/
@ColorInt
public static int opaqueColor(@ColorInt int color) {
int alpha = Color.alpha(color);
if (alpha == 0) return color;
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(255, red, green, blue);
}
}
\ No newline at end of file
/*
* Copyright 2017 Yan Zhenjie.
*
* 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.sobot.album.widget.divider;
import androidx.recyclerview.widget.RecyclerView;
public abstract class Divider extends RecyclerView.ItemDecoration {
/**
* Get the height of the divider.
*
* @return height of the divider.
*/
public abstract int getHeight();
/**
* Get the width of the divider.
*
* @return width of the divider.
*/
public abstract int getWidth();
}
\ No newline at end of file
/*
* Copyright 2018 Yan Zhenjie.
*
* 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.sobot.album.widget.divider;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.view.View;
class Drawer {
private final Drawable mDivider;
private final int mWidth;
private final int mHeight;
public Drawer(Drawable divider, int width, int height) {
this.mDivider = divider;
this.mWidth = width;
this.mHeight = height;
}
/**
* Draw the divider on the left side of the Item.
*/
public void drawLeft(View view, Canvas c) {
int left = view.getLeft() - mWidth;
int top = view.getTop() - mHeight;
int right = left + mWidth;
int bottom = view.getBottom() + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
/**
* Draw the divider on the top side of the Item.
*/
public void drawTop(View view, Canvas c) {
int left = view.getLeft() - mWidth;
int top = view.getTop() - mHeight;
int right = view.getRight() + mWidth;
int bottom = top + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
/**
* Draw the divider on the top side of the Item.
*/
public void drawRight(View view, Canvas c) {
int left = view.getRight();
int top = view.getTop() - mHeight;
int right = left + mWidth;
int bottom = view.getBottom() + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
/**
* Draw the divider on the top side of the Item.
*/
public void drawBottom(View view, Canvas c) {
int left = view.getLeft() - mWidth;
int top = view.getBottom();
int right = view.getRight() + mWidth;
int bottom = top + mHeight;
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
\ No newline at end of file
package com.sobot.album.widget.photoview;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
/**
* Created by Yan Zhenjie on 2017/3/31.
*/
public class AttacherImageView extends androidx.appcompat.widget.AppCompatImageView {
private PhotoViewAttacher mAttacher;
public AttacherImageView(Context context) {
super(context);
}
public AttacherImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AttacherImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setAttacher(PhotoViewAttacher attacher) {
this.mAttacher = attacher;
}
@Override
public void setImageDrawable(@Nullable Drawable drawable) {
super.setImageDrawable(drawable);
if (mAttacher != null) {
mAttacher.update();
}
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview;
import android.annotation.TargetApi;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.view.MotionEvent;
import android.view.View;
public class Compat {
private static final int SIXTY_FPS_INTERVAL = 1000 / 60;
public static void postOnAnimation(View view, Runnable runnable) {
if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
postOnAnimationJellyBean(view, runnable);
} else {
view.postDelayed(runnable, SIXTY_FPS_INTERVAL);
}
}
@TargetApi(16)
private static void postOnAnimationJellyBean(View view, Runnable runnable) {
view.postOnAnimation(runnable);
}
public static int getPointerIndex(int action) {
return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview;
import android.graphics.RectF;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.ImageView;
/**
* Provided default implementation of GestureDetector.OnDoubleTapListener, to be overridden with custom behavior, if needed
* <p>&nbsp;</p>
* To be used via {@link PhotoViewAttacher#setOnDoubleTapListener(GestureDetector.OnDoubleTapListener)}
*/
public class DefaultOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {
private PhotoViewAttacher photoViewAttacher;
/**
* Default constructor
*
* @param photoViewAttacher PhotoViewAttacher to bind to
*/
public DefaultOnDoubleTapListener(PhotoViewAttacher photoViewAttacher) {
setPhotoViewAttacher(photoViewAttacher);
}
/**
* Allows to change PhotoViewAttacher within range of single instance
*
* @param newPhotoViewAttacher PhotoViewAttacher to bind to
*/
public void setPhotoViewAttacher(PhotoViewAttacher newPhotoViewAttacher) {
this.photoViewAttacher = newPhotoViewAttacher;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (this.photoViewAttacher == null)
return false;
ImageView imageView = photoViewAttacher.getImageView();
if (null != photoViewAttacher.getOnPhotoTapListener()) {
final RectF displayRect = photoViewAttacher.getDisplayRect();
if (null != displayRect) {
final float x = e.getX(), y = e.getY();
// Check to see if the user tapped on the photo
if (displayRect.contains(x, y)) {
float xResult = (x - displayRect.left)
/ displayRect.width();
float yResult = (y - displayRect.top)
/ displayRect.height();
photoViewAttacher.getOnPhotoTapListener().onPhotoTap(imageView, xResult, yResult);
return true;
} else {
photoViewAttacher.getOnPhotoTapListener().onOutsidePhotoTap();
}
}
}
if (null != photoViewAttacher.getOnViewTapListener()) {
photoViewAttacher.getOnViewTapListener().onViewTap(imageView, e.getX(), e.getY());
}
return false;
}
@Override
public boolean onDoubleTap(MotionEvent ev) {
if (photoViewAttacher == null)
return false;
try {
float scale = photoViewAttacher.getScale();
float x = ev.getX();
float y = ev.getY();
if (scale < photoViewAttacher.getMediumScale()) {
photoViewAttacher.setScale(photoViewAttacher.getMediumScale(), x, y, true);
} else if (scale >= photoViewAttacher.getMediumScale() && scale < photoViewAttacher.getMaximumScale()) {
photoViewAttacher.setScale(photoViewAttacher.getMaximumScale(), x, y, true);
} else {
photoViewAttacher.setScale(photoViewAttacher.getMinimumScale(), x, y, true);
}
} catch (ArrayIndexOutOfBoundsException e) {
// Can sometimes happen when getX() and getY() is called
}
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
// Wait for the confirmed onDoubleTap() instead
return false;
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.viewpager.widget.ViewPager;
/**
* Created by Yan Zhenjie on 2016/11/1.
*/
public class FixViewPager extends ViewPager {
public FixViewPager(Context context) {
super(context);
}
public FixViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (IllegalArgumentException e) {
return false;
}
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.view.GestureDetector;
import android.view.View;
import android.widget.ImageView;
public interface IPhotoView {
float DEFAULT_MAX_SCALE = 3.0f;
float DEFAULT_MID_SCALE = 1.75f;
float DEFAULT_MIN_SCALE = 1.0f;
int DEFAULT_ZOOM_DURATION = 200;
/**
* Returns true if the PhotoView is set to allow zooming of Photos.
*
* @return true if the PhotoView allows zooming.
*/
boolean canZoom();
/**
* Gets the Display Rectangle of the currently displayed Drawable. The Rectangle is relative to
* this View and includes all scaling and translations.
*
* @return - RectF of Displayed Drawable
*/
RectF getDisplayRect();
/**
* Sets the Display Matrix of the currently displayed Drawable. The Rectangle is considered
* relative to this View and includes all scaling and translations.
*
* @param finalMatrix target matrix to set PhotoView to
* @return - true if rectangle was applied successfully
*/
boolean setDisplayMatrix(Matrix finalMatrix);
/**
* Copies the Display Matrix of the currently displayed Drawable. The Rectangle is considered
* relative to this View and includes all scaling and translations.
*
* @param matrix target matrix to copy to
*/
void getDisplayMatrix(Matrix matrix);
/**
* @return The current minimum scale level. What this value represents depends on the current
* {@link ImageView.ScaleType}.
*/
float getMinimumScale();
/**
* @return The current medium scale level. What this value represents depends on the current
* {@link ImageView.ScaleType}.
*/
float getMediumScale();
/**
* @return The current maximum scale level. What this value represents depends on the current
* {@link ImageView.ScaleType}.
*/
float getMaximumScale();
/**
* Returns the current scale value
*
* @return float - current scale value
*/
float getScale();
/**
* Return the current scale type in use by the ImageView.
*
* @return current ImageView.ScaleType
*/
ImageView.ScaleType getScaleType();
/**
* Whether to allow the ImageView's parent to intercept the touch event when the photo is scroll
* to it's horizontal edge.
*
* @param allow whether to allow intercepting by parent element or not
*/
void setAllowParentInterceptOnEdge(boolean allow);
/**
* Sets the minimum scale level. What this value represents depends on the current {@link
* ImageView.ScaleType}.
*
* @param minimumScale minimum allowed scale
*/
void setMinimumScale(float minimumScale);
/**
* Sets the medium scale level. What this value represents depends on the current {@link ImageView.ScaleType}.
*
* @param mediumScale medium scale preset
*/
void setMediumScale(float mediumScale);
/**
* Sets the maximum scale level. What this value represents depends on the current {@link
* ImageView.ScaleType}.
*
* @param maximumScale maximum allowed scale preset
*/
void setMaximumScale(float maximumScale);
/**
* Allows to set all three scale levels at once, so you don't run into problem with setting
* medium/minimum scale before the maximum one
*
* @param minimumScale minimum allowed scale
* @param mediumScale medium allowed scale
* @param maximumScale maximum allowed scale preset
*/
void setScaleLevels(float minimumScale, float mediumScale, float maximumScale);
/**
* Register a callback to be invoked when the Photo displayed by this view is long-pressed.
*
* @param listener - Listener to be registered.
*/
void setOnLongClickListener(View.OnLongClickListener listener);
/**
* Register a callback to be invoked when the Matrix has changed for this View. An example would
* be the user panning or scaling the Photo.
*
* @param listener - Listener to be registered.
*/
void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener);
/**
* Register a callback to be invoked when the Photo displayed by this View is tapped with a
* single tap.
*
* @param listener - Listener to be registered.
*/
void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener);
/**
* Register a callback to be invoked when the View is tapped with a single tap.
*
* @param listener - Listener to be registered.
*/
void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener);
/**
* Enables rotation via PhotoView internal functions.
*
* @param rotationDegree - Degree to rotate PhotoView to, should be in range 0 to 360
*/
void setRotationTo(float rotationDegree);
/**
* Enables rotation via PhotoView internal functions.
*
* @param rotationDegree - Degree to rotate PhotoView by, should be in range 0 to 360
*/
void setRotationBy(float rotationDegree);
/**
* Changes the current scale to the specified value.
*
* @param scale - Value to scale to
*/
void setScale(float scale);
/**
* Changes the current scale to the specified value.
*
* @param scale - Value to scale to
* @param animate - Whether to animate the scale
*/
void setScale(float scale, boolean animate);
/**
* Changes the current scale to the specified value, around the given focal point.
*
* @param scale - Value to scale to
* @param focalX - X Focus Point
* @param focalY - Y Focus Point
* @param animate - Whether to animate the scale
*/
void setScale(float scale, float focalX, float focalY, boolean animate);
/**
* Controls how the image should be resized or moved to match the size of the ImageView. Any
* scaling or panning will happen within the confines of this {@link
* ImageView.ScaleType}.
*
* @param scaleType - The desired scaling mode.
*/
void setScaleType(ImageView.ScaleType scaleType);
/**
* Allows you to enable/disable the zoom functionality on the ImageView. When disable the
* ImageView reverts to using the FIT_CENTER matrix.
*
* @param zoomable - Whether the zoom functionality is enabled.
*/
void setZoomable(boolean zoomable);
/**
* Extracts currently visible area to Bitmap object, if there is no image loaded yet or the
* ImageView is already destroyed, returns {@code null}
*
* @return currently visible area as bitmap or null
*/
Bitmap getVisibleRectangleBitmap();
/**
* Allows to change zoom transition speed, default value is 200 (PhotoViewAttacher.DEFAULT_ZOOM_DURATION).
* Will default to 200 if provided negative value
*
* @param milliseconds duration of zoom interpolation
*/
void setZoomTransitionDuration(int milliseconds);
/**
* Will return instance of IPhotoView (eg. PhotoViewAttacher), can be used to provide better
* integration
*
* @return IPhotoView implementation instance if available, null if not
*/
IPhotoView getIPhotoViewImplementation();
/**
* Sets custom double tap listener, to intercept default given functions. To reset behavior to
* default, you can just pass in "null" or public field of PhotoViewAttacher.defaultOnDoubleTapListener
*
* @param newOnDoubleTapListener custom OnDoubleTapListener to be set on ImageView
*/
void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener);
/**
* Will report back about scale changes
*
* @param onScaleChangeListener OnScaleChangeListener instance
*/
void setOnScaleChangeListener(PhotoViewAttacher.OnScaleChangeListener onScaleChangeListener);
/**
* Will report back about fling(single touch)
*
* @param onSingleFlingListener OnSingleFlingListener instance
*/
void setOnSingleFlingListener(PhotoViewAttacher.OnSingleFlingListener onSingleFlingListener);
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview;
import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Matrix.ScaleToFit;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import androidx.annotation.Nullable;
import androidx.core.view.MotionEventCompat;
import com.sobot.album.widget.photoview.gestures.OnGestureListener;
import com.sobot.album.widget.photoview.gestures.VersionedGestureDetector;
import com.sobot.album.widget.photoview.scrollerproxy.ScrollerProxy;
import java.lang.ref.WeakReference;
public class PhotoViewAttacher
implements IPhotoView, View.OnTouchListener, OnGestureListener, ViewTreeObserver.OnGlobalLayoutListener {
private Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
int ZOOM_DURATION = DEFAULT_ZOOM_DURATION;
static final int EDGE_NONE = -1;
static final int EDGE_LEFT = 0;
static final int EDGE_RIGHT = 1;
static final int EDGE_BOTH = 2;
static int SINGLE_TOUCH = 1;
private float mMinScale = DEFAULT_MIN_SCALE;
private float mMidScale = DEFAULT_MID_SCALE;
private float mMaxScale = DEFAULT_MAX_SCALE;
private boolean mAllowParentInterceptOnEdge = true;
private boolean mBlockParentIntercept = false;
private static void checkZoomLevels(float minZoom, float midZoom, float maxZoom) {
if (minZoom >= midZoom) {
throw new IllegalArgumentException(
"Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value");
} else if (midZoom >= maxZoom) {
throw new IllegalArgumentException(
"Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value");
}
}
/**
* @return true if the ImageView exists, and its Drawable exists
*/
private static boolean hasDrawable(ImageView imageView) {
return null != imageView && null != imageView.getDrawable();
}
/**
* @return true if the ScaleType is supported.
*/
private static boolean isSupportedScaleType(final ScaleType scaleType) {
if (null == scaleType) {
return false;
}
switch (scaleType) {
case MATRIX:
throw new IllegalArgumentException(scaleType.name() + " is not supported in PhotoView");
default:
return true;
}
}
/**
* Sets the ImageView's ScaleType to Matrix.
*/
private static void setImageViewScaleTypeMatrix(ImageView imageView) {
/**
* PhotoView sets its own ScaleType to Matrix, then diverts all calls
* setScaleType to this.setScaleType automatically.
*/
if (null != imageView && !(imageView instanceof IPhotoView)) {
if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
imageView.setScaleType(ScaleType.MATRIX);
}
}
}
private WeakReference<ImageView> mImageView;
// Gesture Detectors
private GestureDetector mGestureDetector;
private com.sobot.album.widget.photoview.gestures.GestureDetector mScaleDragDetector;
// These are set so we don't keep allocating them on the heap
private final Matrix mBaseMatrix = new Matrix();
private final Matrix mDrawMatrix = new Matrix();
private final Matrix mSuppMatrix = new Matrix();
private final RectF mDisplayRect = new RectF();
private final float[] mMatrixValues = new float[9];
// Listeners
private OnMatrixChangedListener mMatrixChangeListener;
private OnPhotoTapListener mPhotoTapListener;
private OnViewTapListener mViewTapListener;
private OnLongClickListener mLongClickListener;
private OnScaleChangeListener mScaleChangeListener;
private OnSingleFlingListener mSingleFlingListener;
private int mIvTop, mIvRight, mIvBottom, mIvLeft;
private FlingRunnable mCurrentFlingRunnable;
private int mScrollEdge = EDGE_BOTH;
private float mBaseRotation;
private boolean mZoomEnabled;
private ScaleType mScaleType = ScaleType.FIT_CENTER;
public PhotoViewAttacher(ImageView imageView) {
this(imageView, true);
}
public PhotoViewAttacher(ImageView imageView, boolean zoomable) {
mImageView = new WeakReference<>(imageView);
imageView.setDrawingCacheEnabled(true);
imageView.setOnTouchListener(this);
ViewTreeObserver observer = imageView.getViewTreeObserver();
if (null != observer) observer.addOnGlobalLayoutListener(this);
// Make sure we using MATRIX Scale Type
setImageViewScaleTypeMatrix(imageView);
if (imageView.isInEditMode()) {
return;
}
// Create Gesture Detectors...
mScaleDragDetector = VersionedGestureDetector.newInstance(imageView.getContext(), this);
mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() {
// forward long click listener
@Override
public void onLongPress(MotionEvent e) {
if (null != mLongClickListener) {
mLongClickListener.onLongClick(getImageView());
}
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if (mSingleFlingListener != null) {
if (getScale() > DEFAULT_MIN_SCALE) {
return false;
}
if (MotionEventCompat.getPointerCount(e1) > SINGLE_TOUCH ||
MotionEventCompat.getPointerCount(e2) > SINGLE_TOUCH) {
return false;
}
return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY);
}
return false;
}
});
mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
mBaseRotation = 0.0f;
// Finally, update the UI so that we're zoomable
setZoomable(zoomable);
}
@Override
public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) {
if (newOnDoubleTapListener != null) {
this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener);
} else {
this.mGestureDetector.setOnDoubleTapListener(new DefaultOnDoubleTapListener(this));
}
}
@Override
public void setOnScaleChangeListener(OnScaleChangeListener onScaleChangeListener) {
this.mScaleChangeListener = onScaleChangeListener;
}
@Override
public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) {
this.mSingleFlingListener = onSingleFlingListener;
}
@Override
public boolean canZoom() {
return mZoomEnabled;
}
/**
* Clean-up the resources attached to this object. This needs to be called when the ImageView is no longer used. A
* good example is from {@link View# onDetachedFromWindow ()} or from {@link android.app.Activity# onDestroy()}. This
* is automatically called if you are using {@link IPhotoView}.
*/
@SuppressWarnings("deprecation")
public void cleanup() {
if (null == mImageView) {
return; // cleanup already done
}
final ImageView imageView = mImageView.get();
if (null != imageView) {
// Remove this as a global layout listener
ViewTreeObserver observer = imageView.getViewTreeObserver();
if (null != observer && observer.isAlive()) {
observer.removeGlobalOnLayoutListener(this);
}
// Remove the ImageView's reference to this
imageView.setOnTouchListener(null);
// make sure a pending fling runnable won't be run
cancelFling();
}
if (null != mGestureDetector) {
mGestureDetector.setOnDoubleTapListener(null);
}
// Clear listeners too
mMatrixChangeListener = null;
mPhotoTapListener = null;
mViewTapListener = null;
// Finally, clear ImageView
mImageView = null;
}
@Override
public RectF getDisplayRect() {
checkMatrixBounds();
return getDisplayRect(getDrawMatrix());
}
@Override
public boolean setDisplayMatrix(Matrix finalMatrix) {
if (finalMatrix == null) {
throw new IllegalArgumentException("Matrix cannot be null");
}
ImageView imageView = getImageView();
if (null == imageView) {
return false;
}
if (null == imageView.getDrawable()) {
return false;
}
mSuppMatrix.set(finalMatrix);
setImageViewMatrix(getDrawMatrix());
checkMatrixBounds();
return true;
}
public void setBaseRotation(final float degrees) {
mBaseRotation = degrees % 360;
update();
setRotationBy(mBaseRotation);
checkAndDisplayMatrix();
}
@Override
public void setRotationTo(float degrees) {
mSuppMatrix.setRotate(degrees % 360);
checkAndDisplayMatrix();
}
@Override
public void setRotationBy(float degrees) {
mSuppMatrix.postRotate(degrees % 360);
checkAndDisplayMatrix();
}
public ImageView getImageView() {
ImageView imageView = null;
if (null != mImageView) {
imageView = mImageView.get();
}
// If we don't have an ImageView, call cleanup()
if (null == imageView) {
cleanup();
}
return imageView;
}
@Override
public float getMinimumScale() {
return mMinScale;
}
@Override
public float getMediumScale() {
return mMidScale;
}
@Override
public float getMaximumScale() {
return mMaxScale;
}
@Override
public float getScale() {
return (float)Math.sqrt((float)Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) +
(float)Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2));
}
@Override
public ScaleType getScaleType() {
return mScaleType;
}
@Override
public void onDrag(float dx, float dy) {
if (mScaleDragDetector.isScaling()) {
return; // Do not drag if we are already scaling
}
ImageView imageView = getImageView();
mSuppMatrix.postTranslate(dx, dy);
checkAndDisplayMatrix();
/**
* Here we decide whether to let the ImageView's parent to start taking
* over the touch event.
*
* First we check whether this function is enabled. We never want the
* parent to take over if we're scaling. We then check the edge we're
* on, and the direction of the scroll (i.e. if we're pulling against
* the edge, aka 'overscrolling', let the parent take over).
*/
ViewParent parent = imageView.getParent();
if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) {
if (mScrollEdge == EDGE_BOTH || (mScrollEdge == EDGE_LEFT && dx >= 1f) ||
(mScrollEdge == EDGE_RIGHT && dx <= -1f)) {
if (null != parent) {
parent.requestDisallowInterceptTouchEvent(false);
}
}
} else {
if (null != parent) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
}
@Override
public void onFling(float startX, float startY, float velocityX, float velocityY) {
ImageView imageView = getImageView();
mCurrentFlingRunnable = new FlingRunnable(imageView.getContext());
mCurrentFlingRunnable.fling(getImageViewWidth(imageView), getImageViewHeight(imageView), (int)velocityX,
(int)velocityY);
imageView.post(mCurrentFlingRunnable);
}
@Override
public void onGlobalLayout() {
ImageView imageView = getImageView();
if (null != imageView) {
if (mZoomEnabled) {
final int top = imageView.getTop();
final int right = imageView.getRight();
final int bottom = imageView.getBottom();
final int left = imageView.getLeft();
/**
* We need to check whether the ImageView's bounds have changed.
* This would be easier if we targeted API 11+ as we could just use
* View.OnLayoutChangeListener. Instead we have to replicate the
* work, keeping track of the ImageView's bounds and then checking
* if the values change.
*/
if (top != mIvTop || bottom != mIvBottom || left != mIvLeft || right != mIvRight) {
// Update our base matrix, as the bounds have changed
updateBaseMatrix(imageView.getDrawable());
// Update values as something has changed
mIvTop = top;
mIvRight = right;
mIvBottom = bottom;
mIvLeft = left;
}
} else {
updateBaseMatrix(imageView.getDrawable());
}
}
}
@Override
public void onScale(float scaleFactor, float focusX, float focusY) {
if ((getScale() < mMaxScale || scaleFactor < 1f) && (getScale() > mMinScale || scaleFactor > 1f)) {
if (null != mScaleChangeListener) {
mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY);
}
mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY);
checkAndDisplayMatrix();
}
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View v, MotionEvent ev) {
boolean handled = false;
if (mZoomEnabled && hasDrawable((ImageView)v)) {
ViewParent parent = v.getParent();
switch (ev.getAction()) {
case ACTION_DOWN:
// First, disable the Parent from intercepting the touch
// event
if (null != parent) {
parent.requestDisallowInterceptTouchEvent(true);
}
// If we're flinging, and the user presses down, cancel
// fling
cancelFling();
break;
case ACTION_CANCEL:
case ACTION_UP:
// If the user has zoomed less than min scale, zoom back
// to min scale
if (getScale() < mMinScale) {
RectF rect = getDisplayRect();
if (null != rect) {
v.post(new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), rect.centerY()));
handled = true;
}
}
break;
}
// Try the Scale/Drag detector
if (null != mScaleDragDetector) {
boolean wasScaling = mScaleDragDetector.isScaling();
boolean wasDragging = mScaleDragDetector.isDragging();
handled = mScaleDragDetector.onTouchEvent(ev);
boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling();
boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging();
mBlockParentIntercept = didntScale && didntDrag;
}
// Check to see if the user double tapped
if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) {
handled = true;
}
}
return handled;
}
@Override
public void setAllowParentInterceptOnEdge(boolean allow) {
mAllowParentInterceptOnEdge = allow;
}
@Override
public void setMinimumScale(float minimumScale) {
checkZoomLevels(minimumScale, mMidScale, mMaxScale);
mMinScale = minimumScale;
}
@Override
public void setMediumScale(float mediumScale) {
checkZoomLevels(mMinScale, mediumScale, mMaxScale);
mMidScale = mediumScale;
}
@Override
public void setMaximumScale(float maximumScale) {
checkZoomLevels(mMinScale, mMidScale, maximumScale);
mMaxScale = maximumScale;
}
@Override
public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) {
checkZoomLevels(minimumScale, mediumScale, maximumScale);
mMinScale = minimumScale;
mMidScale = mediumScale;
mMaxScale = maximumScale;
}
@Override
public void setOnLongClickListener(OnLongClickListener listener) {
mLongClickListener = listener;
}
@Override
public void setOnMatrixChangeListener(OnMatrixChangedListener listener) {
mMatrixChangeListener = listener;
}
@Override
public void setOnPhotoTapListener(OnPhotoTapListener listener) {
mPhotoTapListener = listener;
}
@Nullable
OnPhotoTapListener getOnPhotoTapListener() {
return mPhotoTapListener;
}
@Override
public void setOnViewTapListener(OnViewTapListener listener) {
mViewTapListener = listener;
}
@Nullable
OnViewTapListener getOnViewTapListener() {
return mViewTapListener;
}
@Override
public void setScale(float scale) {
setScale(scale, false);
}
@Override
public void setScale(float scale, boolean animate) {
ImageView imageView = getImageView();
if (null != imageView) {
setScale(scale, (imageView.getRight()) / 2, (imageView.getBottom()) / 2, animate);
}
}
@Override
public void setScale(float scale, float focalX, float focalY, boolean animate) {
ImageView imageView = getImageView();
if (null != imageView) {
if (scale < mMinScale || scale > mMaxScale) {
return;
}
if (animate) {
imageView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY));
} else {
mSuppMatrix.setScale(scale, scale, focalX, focalY);
checkAndDisplayMatrix();
}
}
}
/**
* Set the zoom interpolator
*
* @param interpolator the zoom interpolator
*/
public void setZoomInterpolator(Interpolator interpolator) {
mInterpolator = interpolator;
}
@Override
public void setScaleType(ScaleType scaleType) {
if (isSupportedScaleType(scaleType) && scaleType != mScaleType) {
mScaleType = scaleType;
// Finally update
update();
}
}
@Override
public void setZoomable(boolean zoomable) {
mZoomEnabled = zoomable;
update();
}
public void update() {
ImageView imageView = getImageView();
if (null != imageView) {
if (mZoomEnabled) {
// Make sure we using MATRIX Scale Type
setImageViewScaleTypeMatrix(imageView);
// Update the base matrix using the current drawable
updateBaseMatrix(imageView.getDrawable());
} else {
// Reset the Matrix...
resetMatrix();
}
}
}
/**
* Get the display matrix
*
* @param matrix target matrix to copy to
*/
@Override
public void getDisplayMatrix(Matrix matrix) {
matrix.set(getDrawMatrix());
}
/**
* Get the current support matrix
*/
public void getSuppMatrix(Matrix matrix) {
matrix.set(mSuppMatrix);
}
private Matrix getDrawMatrix() {
mDrawMatrix.set(mBaseMatrix);
mDrawMatrix.postConcat(mSuppMatrix);
return mDrawMatrix;
}
private void cancelFling() {
if (null != mCurrentFlingRunnable) {
mCurrentFlingRunnable.cancelFling();
mCurrentFlingRunnable = null;
}
}
public Matrix getImageMatrix() {
return mDrawMatrix;
}
/**
* Helper method that simply checks the Matrix, and then displays the result
*/
private void checkAndDisplayMatrix() {
if (checkMatrixBounds()) {
setImageViewMatrix(getDrawMatrix());
}
}
private void checkImageViewScaleType() {
ImageView imageView = getImageView();
/**
* PhotoView's getScaleType() will just divert to this.getScaleType() so
* only call if we're not attached to a PhotoView.
*/
if (null != imageView && !(imageView instanceof IPhotoView)) {
if (!ScaleType.MATRIX.equals(imageView.getScaleType())) {
throw new IllegalStateException(
"The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher. You should call " +
"setScaleType on the PhotoViewAttacher instead of on the ImageView");
}
}
}
private boolean checkMatrixBounds() {
final ImageView imageView = getImageView();
if (null == imageView) {
return false;
}
final RectF rect = getDisplayRect(getDrawMatrix());
if (null == rect) {
return false;
}
final float height = rect.height(), width = rect.width();
float deltaX = 0, deltaY = 0;
final int viewHeight = getImageViewHeight(imageView);
if (height <= viewHeight) {
switch (mScaleType) {
case FIT_START:
deltaY = -rect.top;
break;
case FIT_END:
deltaY = viewHeight - height - rect.top;
break;
default:
deltaY = (viewHeight - height) / 2 - rect.top;
break;
}
} else if (rect.top > 0) {
deltaY = -rect.top;
} else if (rect.bottom < viewHeight) {
deltaY = viewHeight - rect.bottom;
}
final int viewWidth = getImageViewWidth(imageView);
if (width <= viewWidth) {
switch (mScaleType) {
case FIT_START:
deltaX = -rect.left;
break;
case FIT_END:
deltaX = viewWidth - width - rect.left;
break;
default:
deltaX = (viewWidth - width) / 2 - rect.left;
break;
}
mScrollEdge = EDGE_BOTH;
} else if (rect.left > 0) {
mScrollEdge = EDGE_LEFT;
deltaX = -rect.left;
} else if (rect.right < viewWidth) {
deltaX = viewWidth - rect.right;
mScrollEdge = EDGE_RIGHT;
} else {
mScrollEdge = EDGE_NONE;
}
// Finally actually translate the matrix
mSuppMatrix.postTranslate(deltaX, deltaY);
return true;
}
/**
* Helper method that maps the supplied Matrix to the current Drawable
*
* @param matrix - Matrix to map Drawable against
*
* @return RectF - Displayed Rectangle
*/
private RectF getDisplayRect(Matrix matrix) {
ImageView imageView = getImageView();
if (null != imageView) {
Drawable d = imageView.getDrawable();
if (null != d) {
mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
matrix.mapRect(mDisplayRect);
return mDisplayRect;
}
}
return null;
}
public Bitmap getVisibleRectangleBitmap() {
ImageView imageView = getImageView();
return imageView == null ? null : imageView.getDrawingCache();
}
@Override
public void setZoomTransitionDuration(int milliseconds) {
if (milliseconds < 0) milliseconds = DEFAULT_ZOOM_DURATION;
this.ZOOM_DURATION = milliseconds;
}
@Override
public IPhotoView getIPhotoViewImplementation() {
return this;
}
/**
* Helper method that 'unpacks' a Matrix and returns the required value
*
* @param matrix - Matrix to unpack
* @param whichValue - Which value from Matrix.M* to return
*
* @return float - returned value
*/
private float getValue(Matrix matrix, int whichValue) {
matrix.getValues(mMatrixValues);
return mMatrixValues[whichValue];
}
/**
* Resets the Matrix back to FIT_CENTER, and then displays it.s
*/
private void resetMatrix() {
mSuppMatrix.reset();
setRotationBy(mBaseRotation);
setImageViewMatrix(getDrawMatrix());
checkMatrixBounds();
}
private void setImageViewMatrix(Matrix matrix) {
ImageView imageView = getImageView();
if (null != imageView) {
checkImageViewScaleType();
imageView.setImageMatrix(matrix);
// Call MatrixChangedListener if needed
if (null != mMatrixChangeListener) {
RectF displayRect = getDisplayRect(matrix);
if (null != displayRect) {
mMatrixChangeListener.onMatrixChanged(displayRect);
}
}
}
}
/**
* Calculate Matrix for FIT_CENTER
*
* @param d - Drawable being displayed
*/
private void updateBaseMatrix(Drawable d) {
ImageView imageView = getImageView();
if (null == imageView || null == d) {
return;
}
final float viewWidth = getImageViewWidth(imageView);
final float viewHeight = getImageViewHeight(imageView);
final int drawableWidth = d.getIntrinsicWidth();
final int drawableHeight = d.getIntrinsicHeight();
mBaseMatrix.reset();
final float widthScale = viewWidth / drawableWidth;
final float heightScale = viewHeight / drawableHeight;
if (mScaleType == ScaleType.CENTER) {
mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F, (viewHeight - drawableHeight) / 2F);
} else if (mScaleType == ScaleType.CENTER_CROP) {
float scale = Math.max(widthScale, heightScale);
mBaseMatrix.postScale(scale, scale);
mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
(viewHeight - drawableHeight * scale) / 2F);
} else if (mScaleType == ScaleType.CENTER_INSIDE) {
float scale = Math.min(1.0f, Math.min(widthScale, heightScale));
mBaseMatrix.postScale(scale, scale);
mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F,
(viewHeight - drawableHeight * scale) / 2F);
} else {
RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight);
RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight);
if ((int)mBaseRotation % 180 != 0) {
mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth);
}
switch (mScaleType) {
case FIT_CENTER:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER);
break;
case FIT_START:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START);
break;
case FIT_END:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END);
break;
case FIT_XY:
mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL);
break;
default:
break;
}
}
resetMatrix();
}
private int getImageViewWidth(ImageView imageView) {
if (null == imageView) return 0;
return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight();
}
private int getImageViewHeight(ImageView imageView) {
if (null == imageView) return 0;
return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom();
}
/**
* Interface definition for a callback to be invoked when the internal Matrix has changed for this View.
*
* @author Chris Banes
*/
public interface OnMatrixChangedListener {
/**
* AlbumCallback for when the Matrix displaying the Drawable has changed. This could be because the View's
* bounds have changed, or the user has zoomed.
*
* @param rect - Rectangle displaying the Drawable's new bounds.
*/
void onMatrixChanged(RectF rect);
}
/**
* Interface definition for callback to be invoked when attached ImageView scale changes
*
* @author Marek Sebera
*/
public interface OnScaleChangeListener {
/**
* AlbumCallback for when the scale changes
*
* @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in)
* @param focusX focal point X position
* @param focusY focal point Y position
*/
void onScaleChange(float scaleFactor, float focusX, float focusY);
}
/**
* Interface definition for a callback to be invoked when the Photo is tapped with a single tap.
*
* @author Chris Banes
*/
public interface OnPhotoTapListener {
/**
* A callback to receive where the user taps on a photo. You will only receive a callback if the user taps on
* the actual photo, tapping on 'whitespace' will be ignored.
*
* @param view - View the user tapped.
* @param x - where the user tapped from the of the Drawable, as percentage of the Drawable width.
* @param y - where the user tapped from the top of the Drawable, as percentage of the Drawable height.
*/
void onPhotoTap(View view, float x, float y);
/**
* A simple callback where out of photo happened;
*/
void onOutsidePhotoTap();
}
/**
* Interface definition for a callback to be invoked when the ImageView is tapped with a single tap.
*
* @author Chris Banes
*/
public interface OnViewTapListener {
/**
* A callback to receive where the user taps on a ImageView. You will receive a callback if the user taps
* anywhere on the view, tapping on 'whitespace' will not be ignored.
*
* @param v - View the user tapped.
* @param x - where the user tapped from the left of the View.
* @param y - where the user tapped from the top of the View.
*/
void onViewTap(View v, float x, float y);
}
/**
* Interface definition for a callback to be invoked when the ImageView is fling with a single touch
*
* @author tonyjs
*/
public interface OnSingleFlingListener {
/**
* A callback to receive where the user flings on a ImageView. You will receive a callback if the user flings
* anywhere on the view.
*
* @param e1 - MotionEvent the user first touch.
* @param e2 - MotionEvent the user last touch.
* @param velocityX - distance of user's horizontal fling.
* @param velocityY - distance of user's vertical fling.
*/
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
private class AnimatedZoomRunnable implements Runnable {
private final float mFocalX, mFocalY;
private final long mStartTime;
private final float mZoomStart, mZoomEnd;
public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX,
final float focalY) {
mFocalX = focalX;
mFocalY = focalY;
mStartTime = System.currentTimeMillis();
mZoomStart = currentZoom;
mZoomEnd = targetZoom;
}
@Override
public void run() {
ImageView imageView = getImageView();
if (imageView == null) {
return;
}
float t = interpolate();
float scale = mZoomStart + t * (mZoomEnd - mZoomStart);
float deltaScale = scale / getScale();
onScale(deltaScale, mFocalX, mFocalY);
// We haven't hit our target scale yet, so post ourselves again
if (t < 1f) {
Compat.postOnAnimation(imageView, this);
}
}
private float interpolate() {
float t = 1f * (System.currentTimeMillis() - mStartTime) / ZOOM_DURATION;
t = Math.min(1f, t);
t = mInterpolator.getInterpolation(t);
return t;
}
}
private class FlingRunnable implements Runnable {
private final ScrollerProxy mScroller;
private int mCurrentX, mCurrentY;
public FlingRunnable(Context context) {
mScroller = ScrollerProxy.getScroller(context);
}
public void cancelFling() {
mScroller.forceFinished(true);
}
public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) {
final RectF rect = getDisplayRect();
if (null == rect) {
return;
}
final int startX = Math.round(-rect.left);
final int minX, maxX, minY, maxY;
if (viewWidth < rect.width()) {
minX = 0;
maxX = Math.round(rect.width() - viewWidth);
} else {
minX = maxX = startX;
}
final int startY = Math.round(-rect.top);
if (viewHeight < rect.height()) {
minY = 0;
maxY = Math.round(rect.height() - viewHeight);
} else {
minY = maxY = startY;
}
mCurrentX = startX;
mCurrentY = startY;
// If we actually can move, fling the scroller
if (startX != maxX || startY != maxY) {
mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0);
}
}
@Override
public void run() {
if (mScroller.isFinished()) {
return; // remaining post that should not be handled
}
ImageView imageView = getImageView();
if (null != imageView && mScroller.computeScrollOffset()) {
final int newX = mScroller.getCurrX();
final int newY = mScroller.getCurrY();
mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY);
setImageViewMatrix(getDrawMatrix());
mCurrentX = newX;
mCurrentY = newY;
// Post On animation
Compat.postOnAnimation(imageView, this);
}
}
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.gestures;
import android.content.Context;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
public class CupcakeGestureDetector implements GestureDetector {
protected OnGestureListener mListener;
float mLastTouchX;
float mLastTouchY;
private final float mTouchSlop;
private final float mMinimumVelocity;
@Override
public void setOnGestureListener(OnGestureListener listener) {
this.mListener = listener;
}
public CupcakeGestureDetector(Context context) {
final ViewConfiguration configuration = ViewConfiguration.get(context);
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mTouchSlop = configuration.getScaledTouchSlop();
}
private VelocityTracker mVelocityTracker;
private boolean mIsDragging;
float getActiveX(MotionEvent ev) {
return ev.getX();
}
float getActiveY(MotionEvent ev) {
return ev.getY();
}
@Override
public boolean isScaling() {
return false;
}
@Override
public boolean isDragging() {
return mIsDragging;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
mVelocityTracker = VelocityTracker.obtain();
if (null != mVelocityTracker) {
mVelocityTracker.addMovement(ev);
}
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
mIsDragging = false;
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = getActiveX(ev);
final float y = getActiveY(ev);
final float dx = x - mLastTouchX, dy = y - mLastTouchY;
if (!mIsDragging) {
// Use Pythagoras to see if drag length is larger than
// touch slop
mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop;
}
if (mIsDragging) {
mListener.onDrag(dx, dy);
mLastTouchX = x;
mLastTouchY = y;
if (null != mVelocityTracker) {
mVelocityTracker.addMovement(ev);
}
}
break;
}
case MotionEvent.ACTION_CANCEL: {
// Recycle Velocity Tracker
if (null != mVelocityTracker) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
case MotionEvent.ACTION_UP: {
if (mIsDragging) {
if (null != mVelocityTracker) {
mLastTouchX = getActiveX(ev);
mLastTouchY = getActiveY(ev);
// Compute velocity within the last 1000ms
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000);
final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker
.getYVelocity();
// If the velocity is greater than minVelocity, call
// listener
if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) {
mListener.onFling(mLastTouchX, mLastTouchY, -vX,
-vY);
}
}
}
// Recycle Velocity Tracker
if (null != mVelocityTracker) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
break;
}
}
return true;
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.gestures;
import android.annotation.TargetApi;
import android.content.Context;
import android.view.MotionEvent;
import com.sobot.album.widget.photoview.Compat;
@TargetApi(5)
public class EclairGestureDetector extends CupcakeGestureDetector {
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private int mActivePointerIndex = 0;
public EclairGestureDetector(Context context) {
super(context);
}
@Override
float getActiveX(MotionEvent ev) {
try {
return ev.getX(mActivePointerIndex);
} catch (Exception e) {
return ev.getX();
}
}
@Override
float getActiveY(MotionEvent ev) {
try {
return ev.getY(mActivePointerIndex);
} catch (Exception e) {
return ev.getY();
}
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mActivePointerId = ev.getPointerId(0);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
// Ignore deprecation, ACTION_POINTER_ID_MASK and
// ACTION_POINTER_ID_SHIFT has same value and are deprecated
// You can have either deprecation or lint target api warning
final int pointerIndex = Compat.getPointerIndex(ev.getAction());
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
}
break;
}
mActivePointerIndex = ev
.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId
: 0);
try {
return super.onTouchEvent(ev);
} catch (IllegalArgumentException e) {
// Fix for support lib bug, happening when onDestroy is
return true;
}
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.gestures;
import android.annotation.TargetApi;
import android.content.Context;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
@TargetApi(8)
public class FroyoGestureDetector extends EclairGestureDetector {
protected final ScaleGestureDetector mDetector;
public FroyoGestureDetector(Context context) {
super(context);
ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactor = detector.getScaleFactor();
if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor))
return false;
mListener.onScale(scaleFactor,
detector.getFocusX(), detector.getFocusY());
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// NO-OP
}
};
mDetector = new ScaleGestureDetector(context, mScaleListener);
}
@Override
public boolean isScaling() {
return mDetector.isInProgress();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
try {
mDetector.onTouchEvent(ev);
return super.onTouchEvent(ev);
} catch (IllegalArgumentException e) {
// Fix for support lib bug, happening when onDestroy is
return true;
}
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.gestures;
import android.view.MotionEvent;
public interface GestureDetector {
boolean onTouchEvent(MotionEvent ev);
boolean isScaling();
boolean isDragging();
void setOnGestureListener(OnGestureListener listener);
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.gestures;
public interface OnGestureListener {
void onDrag(float dx, float dy);
void onFling(float startX, float startY, float velocityX, float velocityY);
void onScale(float scaleFactor, float focusX, float focusY);
}
\ No newline at end of file
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.gestures;
import android.content.Context;
import android.os.Build;
public final class VersionedGestureDetector {
public static GestureDetector newInstance(Context context,
OnGestureListener listener) {
final int sdkVersion = Build.VERSION.SDK_INT;
GestureDetector detector;
if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
detector = new CupcakeGestureDetector(context);
} else if (sdkVersion < Build.VERSION_CODES.FROYO) {
detector = new EclairGestureDetector(context);
} else {
detector = new FroyoGestureDetector(context);
}
detector.setOnGestureListener(listener);
return detector;
}
}
\ No newline at end of file
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.scrollerproxy;
import android.annotation.TargetApi;
import android.content.Context;
import android.widget.OverScroller;
@TargetApi(9)
public class GingerScroller extends ScrollerProxy {
protected final OverScroller mScroller;
public GingerScroller(Context context) {
mScroller = new OverScroller(context);
}
@Override
public boolean computeScrollOffset() {
return mScroller.computeScrollOffset();
}
@Override
public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
int overX, int overY) {
mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY);
}
@Override
public void forceFinished(boolean finished) {
mScroller.forceFinished(finished);
}
@Override
public boolean isFinished() {
return mScroller.isFinished();
}
@Override
public int getCurrX() {
return mScroller.getCurrX();
}
@Override
public int getCurrY() {
return mScroller.getCurrY();
}
}
\ No newline at end of file
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.scrollerproxy;
import android.annotation.TargetApi;
import android.content.Context;
@TargetApi(14)
public class IcsScroller extends GingerScroller {
public IcsScroller(Context context) {
super(context);
}
@Override
public boolean computeScrollOffset() {
return mScroller.computeScrollOffset();
}
}
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.scrollerproxy;
import android.content.Context;
import android.widget.Scroller;
public class PreGingerScroller extends ScrollerProxy {
private final Scroller mScroller;
public PreGingerScroller(Context context) {
mScroller = new Scroller(context);
}
@Override
public boolean computeScrollOffset() {
return mScroller.computeScrollOffset();
}
@Override
public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY,
int overX, int overY) {
mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY);
}
@Override
public void forceFinished(boolean finished) {
mScroller.forceFinished(finished);
}
public boolean isFinished() {
return mScroller.isFinished();
}
@Override
public int getCurrX() {
return mScroller.getCurrX();
}
@Override
public int getCurrY() {
return mScroller.getCurrY();
}
}
\ No newline at end of file
/*
* Copyright © Yan Zhenjie. All Rights Reserved
*
* 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.sobot.album.widget.photoview.scrollerproxy;
import android.content.Context;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
public abstract class ScrollerProxy {
public static ScrollerProxy getScroller(Context context) {
if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) {
return new PreGingerScroller(context);
} else if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {
return new GingerScroller(context);
} else {
return new IcsScroller(context);
}
}
public abstract boolean computeScrollOffset();
public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY,
int maxY, int overX, int overY);
public abstract void forceFinished(boolean finished);
public abstract boolean isFinished();
public abstract int getCurrX();
public abstract int getCurrY();
}
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 请选择要留言的业务 button 样式-->
<item android:drawable="@drawable/sobot_photo_selected" android:state_checked="true" />
<item android:drawable="@drawable/sobot_photo_unselected" />
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/sobot_wo_theme_color" />
<corners android:radius="2dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/sobot_wo_theme_color" />
<corners android:radius="4dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/sobot_wo_theme_unable_color" />
<corners android:radius="4dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
style="@style/Album.MatchParent">
<include layout="@layout/sobot_common_layout_titlebar" />
<LinearLayout
style="@style/Album.MatchParent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_message"
style="@style/Album.WrapContent"
android:layout_margin="@dimen/album_dp_10"
android:text="@string/hint_image_preview_click"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
style="@style/Album.WrapContent.WidthMatchParent"
android:layout_marginTop="@dimen/album_dp_10" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?><!--
Copyright 2017 Yan Zhenjie.
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.
-->
<LinearLayout style="@style/Album.MatchParent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/sobot_common_layout_titlebar" />
<include layout="@layout/activity_list_content" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
style="@style/Album.MatchParent">
<include layout="@layout/sobot_common_layout_titlebar" />
<LinearLayout
style="@style/Album.MatchParent"
android:orientation="vertical"
android:padding="@dimen/album_dp_10">
<TextView
android:id="@+id/tv_message"
style="@style/Album.WrapContent.WidthMatchParent"
android:layout_margin="@dimen/album_dp_10" />
<ImageView
android:id="@+id/image_view"
style="@style/Album.MatchParent"
android:contentDescription="@string/app_name"
android:scaleType="fitCenter" />
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
style="@style/Album.MatchParent">
<include layout="@layout/sobot_common_layout_titlebar"/>
<LinearLayout
style="@style/Album.MatchParent"
android:orientation="vertical">
<Button
android:id="@+id/btn_gallery"
style="@style/Button"
android:text="@string/btn_gallery_preview"/>
<CheckBox
android:id="@+id/checkbox"
style="@style/Album.WrapContent"
android:layout_marginTop="@dimen/album_dp_10"
android:checked="true"
android:text="@string/checkable"/>
</LinearLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Album.MatchParent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_message"
style="@style/Album.WrapContent"
android:layout_margin="@dimen/album_dp_10"
android:text="@string/hint_image_preview_click"
android:visibility="gone" />
<ImageView
android:id="@+id/image_view"
style="@style/Album.WrapContent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_marginTop="@dimen/album_dp_10"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Album.MatchParent"
android:background="@color/albumPageLight">
<include layout="@layout/sobot_common_layout_titlebar"
android:id="@+id/app_bar_layout"/>
<LinearLayout
style="@style/Album.MatchParent"
android:layout_below="@+id/app_bar_layout"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/tv_message"
style="@style/Album.WrapContent"
android:textColor="@color/albumFontHint" />
<Button
android:id="@+id/btn_camera_image"
style="@style/WrapContent"
android:layout_marginTop="@dimen/album_dp_15"
android:drawableLeft="@drawable/album_ic_image_camera_white"
android:drawablePadding="@dimen/album_dp_6"
android:drawableStart="@drawable/album_ic_image_camera_white"
android:gravity="center"
android:text="@string/album_camera_image_capture"
android:textColor="@color/albumFontLight" />
<Button
android:id="@+id/btn_camera_video"
style="@style/WrapContent"
android:layout_marginTop="@dimen/album_dp_15"
android:drawableLeft="@drawable/album_ic_video_camera_white"
android:drawablePadding="@dimen/album_dp_6"
android:drawableStart="@drawable/album_ic_video_camera_white"
android:gravity="center"
android:text="@string/album_camera_video_capture"
android:textColor="@color/albumFontLight" />
</LinearLayout>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@+id/ll_bottom"
android:layout_below="@id/app_bar_layout" />
<LinearLayout
android:id="@+id/ll_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@color/sobot_white"
android:gravity="center_vertical">
<TextView
android:id="@+id/sobot_tv_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingEnd="5dp"
android:paddingRight="5dp"
android:paddingBottom="10dp"
android:text="@string/sobot_yulan"
android:textColor="@color/sobot_wo_new_wenzi_gray1"
android:textSize="14sp" />
<TextView
android:id="@+id/sobot_tv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingEnd="5dp"
android:paddingRight="5dp"
android:paddingBottom="10dp"
android:textColor="@color/sobot_wo_new_wenzi_gray2"
android:textSize="14sp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<TextView
android:id="@+id/sobot_tv_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="8dp"
android:background="@drawable/sobot_bg_theme_color_4dp"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
android:text="@string/sobot_add_photos"
android:textColor="@color/sobot_wo_white_color"
android:textSize="14sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_loading"
style="@style/Album.MatchParent"
android:layout_below="@id/app_bar_layout"
android:background="@color/albumPageLight"
android:gravity="center"
android:visibility="gone">
<com.sobot.album.widget.ColorProgressBar
android:id="@+id/progress_bar"
style="@style/Loading" />
<TextView
style="@style/Album.WrapContent"
android:layout_marginStart="@dimen/album_dp_20"
android:layout_marginLeft="@dimen/album_dp_20"
android:text="@string/album_loading"
android:textColor="@color/albumFontHint"
android:textSize="@dimen/album_sp_16" />
</LinearLayout>
</merge>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rv_content_list"
style="@style/Album.WrapContent.WidthMatchParent"
android:background="@color/albumPageLight" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Album.WrapContent"
android:gravity="center"
android:paddingLeft="@dimen/album_dp_35"
android:paddingTop="@dimen/album_dp_20"
android:paddingRight="@dimen/album_dp_35"
android:paddingBottom="@dimen/album_dp_20">
<com.sobot.album.widget.ColorProgressBar
android:id="@+id/progress_bar"
style="@style/Loading" />
<TextView
android:id="@+id/tv_message"
style="@style/Album.WrapContent"
android:layout_marginStart="@dimen/album_dp_20"
android:layout_marginLeft="@dimen/album_dp_20"
android:textColor="@color/albumFontHint"
android:textSize="@dimen/album_sp_16" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<com.sobot.album.widget.SquareCardView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MatchParent">
<com.sobot.album.widget.SquareImageView
android:id="@+id/iv_album_content_button"
style="@style/Album.MatchParent"
android:contentDescription="@string/album_title"
android:scaleType="center"
android:src="@drawable/album_ic_add_photo_white" />
</com.sobot.album.widget.SquareCardView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<com.sobot.album.widget.SquareCardView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MatchParent">
<RelativeLayout style="@style/Album.MatchParent">
<ImageView
android:id="@+id/iv_album_content_image"
style="@style/Album.MatchParent"
android:contentDescription="@string/album_title"
android:scaleType="centerCrop" />
<com.sobot.album.widget.TransferLayout
style="@style/Album.WrapContent.Transfer"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true">
<CheckBox
android:id="@+id/check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/sobot_bg_checkbox"
android:button="@null" />
</com.sobot.album.widget.TransferLayout>
</RelativeLayout>
<FrameLayout
android:id="@+id/layout_layer"
style="@style/Album.MatchParent"
android:background="@color/albumSheetLayer"
android:visibility="gone">
<TextView
style="@style/Album.WrapContent"
android:layout_gravity="center"
android:text="@string/album_item_unavailable"
android:textColor="@color/albumFontLight"
android:textSize="@dimen/album_sp_16" />
</FrameLayout>
</com.sobot.album.widget.SquareCardView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<com.sobot.album.widget.SquareCardView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MatchParent">
<RelativeLayout style="@style/Album.MatchParent">
<ImageView
android:id="@+id/iv_album_content_image"
style="@style/Album.MatchParent"
android:contentDescription="@string/album_title"
android:scaleType="centerCrop" />
<com.sobot.album.widget.TransferLayout
style="@style/Album.WrapContent.Transfer"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true">
<CheckBox
android:id="@+id/check_box"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:button="@null"
android:background="@drawable/sobot_bg_checkbox"/>
</com.sobot.album.widget.TransferLayout>
<TextView
android:id="@+id/tv_duration"
style="@style/Album.WrapContent"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/album_dp_6"
android:layout_marginLeft="@dimen/album_dp_6"
android:layout_marginStart="@dimen/album_dp_6"
android:drawableLeft="@drawable/album_tag_video_white"
android:drawablePadding="@dimen/album_dp_4"
android:drawableStart="@drawable/album_tag_video_white"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:visibility="gone"
android:textColor="@color/albumFontLight" />
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/sobot_video_fullplay"
android:scaleType="centerInside"
android:layout_centerInParent="true"
/>
</RelativeLayout>
<FrameLayout
android:id="@+id/layout_layer"
style="@style/Album.MatchParent"
android:visibility="gone"
android:background="@color/albumSheetLayer">
<TextView
style="@style/Album.WrapContent"
android:layout_gravity="center"
android:text="@string/album_item_unavailable"
android:textColor="@color/albumFontLight"
android:textSize="@dimen/album_sp_16" />
</FrameLayout>
</com.sobot.album.widget.SquareCardView>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Album.WrapContent.WidthMatchParent"
android:padding="@dimen/album_dp_10">
<ImageView
android:id="@+id/iv_gallery_preview_image"
android:layout_width="@dimen/album_dp_80"
android:layout_height="@dimen/album_dp_80"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:contentDescription="@string/album_title"
android:scaleType="centerCrop"/>
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/rb_gallery_preview_check"
style="@style/Album.WrapContent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:clickable="false"/>
<TextView
android:id="@+id/tv_gallery_preview_title"
style="@style/Album.WrapContent.WidthMatchParent"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/album_dp_10"
android:layout_marginLeft="@dimen/album_dp_10"
android:layout_marginRight="@dimen/album_dp_10"
android:layout_marginStart="@dimen/album_dp_10"
android:layout_toEndOf="@id/iv_gallery_preview_image"
android:layout_toLeftOf="@id/rb_gallery_preview_check"
android:layout_toRightOf="@id/iv_gallery_preview_image"
android:layout_toStartOf="@id/rb_gallery_preview_check"
android:ellipsize="end"
android:maxLines="1"/>
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_bar_layout"
style="@style/Album.AppBar.General">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar" />
</com.google.android.material.appbar.AppBarLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_bar_layout"
style="@style/Album.AppBar.General">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar" />
</com.google.android.material.appbar.AppBarLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<com.sobot.album.widget.photoview.AttacherImageView
android:id="@+id/sobot_item_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:paddingLeft="10dp"
android:paddingRight="10dp">
<VideoView
android:id="@+id/fragment_preview_video"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" />
<ImageView
android:id="@+id/fragment_preview_first_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:visibility="visible"
android:background="@color/sobot_album_transparent"
android:scaleType="fitCenter" />
<ImageView
android:id="@+id/fragment_preview_play_img"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center"
android:src="@drawable/sobot_look_preview_play" />
</FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<com.sobot.album.widget.SquareImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/iv_album_content_image"
style="@style/Album.MatchParent"
android:contentDescription="@string/album_title"
android:scaleType="centerCrop" />
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<com.sobot.album.widget.SquareRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/Album.MatchParent">
<ImageView
android:id="@+id/iv_album_content_image"
style="@style/Album.MatchParent"
android:contentDescription="@string/album_title"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tv_duration"
style="@style/Album.WrapContent"
android:layout_alignParentBottom="true"
android:layout_marginBottom="@dimen/album_dp_6"
android:layout_marginLeft="@dimen/album_dp_6"
android:layout_marginStart="@dimen/album_dp_6"
android:drawableLeft="@drawable/album_tag_video_white"
android:drawablePadding="@dimen/album_dp_4"
android:drawableStart="@drawable/album_tag_video_white"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/albumFontLight" />
</com.sobot.album.widget.SquareRelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<com.sobot.album.widget.SquareRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/sobot_iv_album_content_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:contentDescription="@string/album_title"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/sobot_iv_play_image"
android:layout_width="15dp"
android:layout_height="15dp"
android:src="@drawable/sobot_video_fullplay"
android:contentDescription="@string/album_title"
android:layout_centerInParent="true"
android:scaleType="centerInside" />
</com.sobot.album.widget.SquareRelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<LinearLayout style="@style/Album.MatchParent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<include layout="@layout/sobot_common_layout_titlebar"/>
<include layout="@layout/activity_list_content"/>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MatchParent"
android:background="@color/albumColorPrimaryBlack"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/sobot_layout_titlebar"
android:layout_width="match_parent"
android:layout_height="@dimen/sobot_titlebar_height"
android:background="@color/albumSheetBottom">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:orientation="horizontal">
<TextView
android:id="@+id/sobot_tv_left"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:drawableLeft="@drawable/sobot_icon_back"
android:drawablePadding="5dp"
android:gravity="center_vertical"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:textColor="@color/sobot_color_title_bar_menu_text"
android:textSize="@dimen/sobot_titlebar_title_size" />
<LinearLayout
android:id="@+id/sobot_header_center_ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginStart="80dp"
android:layout_marginLeft="80dp"
android:layout_marginEnd="80dp"
android:layout_marginRight="80dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/sobot_text_title_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:singleLine="true"
android:textColor="@color/sobot_color_title_bar_title"
android:textSize="@dimen/sobot_titlebar_title_size"
android:textStyle="bold"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:id="@+id/sobot_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:orientation="horizontal">
<CheckBox
android:id="@+id/check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/album_dp_10"
android:layout_marginRight="@dimen/album_dp_10"
android:background="@drawable/sobot_bg_checkbox"
android:button="@null"
android:textColor="@color/albumFontLight"
android:textSize="@dimen/album_sp_16" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
<com.sobot.album.widget.photoview.FixViewPager
android:id="@+id/view_pager"
style="@style/MatchParent"
android:layout_weight="1" />
<HorizontalScrollView
android:id="@+id/sobot_hscroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/albumSheetBottom"
android:visibility="gone">
<LinearLayout
android:id="@+id/sobot_thumbnail_ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="6dp" />
</HorizontalScrollView>
<FrameLayout
style="@style/Album.WrapContent.WidthMatchParent"
android:layout_alignParentBottom="true">
<LinearLayout
android:id="@+id/layout_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/albumSheetBottom"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_duration"
style="@style/Album.WrapContent"
android:layout_marginStart="@dimen/album_dp_10"
android:layout_marginLeft="@dimen/album_dp_10"
android:drawableStart="@drawable/album_tag_video_white"
android:drawableLeft="@drawable/album_tag_video_white"
android:drawablePadding="@dimen/album_dp_4"
android:gravity="center"
android:maxLines="1"
android:textColor="@color/sobot_white"
android:visibility="gone" />
<TextView
android:id="@+id/sobot_tv_select"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginStart="@dimen/album_dp_10"
android:layout_marginLeft="@dimen/album_dp_10"
android:layout_marginEnd="5dp"
android:layout_marginRight="5dp"
android:padding="5dp"
android:textColor="@color/sobot_white"
android:textSize="16sp" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<TextView
android:id="@+id/sobot_tv_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="15dp"
android:layout_marginRight="15dp"
android:layout_marginBottom="8dp"
android:background="@drawable/sobot_bg_theme_color_4dp"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
android:text="@string/sobot_add_photos"
android:textColor="@color/sobot_wo_white_color"
android:textSize="16sp" />
</LinearLayout>
<FrameLayout
android:id="@+id/layout_layer"
style="@style/Album.MatchParent.SheetBottom"
android:background="@color/albumSheetLayer"
android:visibility="gone">
<TextView
style="@style/Album.WrapContent"
android:layout_gravity="center"
android:text="@string/album_item_unavailable"
android:textColor="@color/albumFontLight"
android:textSize="@dimen/album_sp_18" />
</FrameLayout>
</FrameLayout>
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/MatchParent"
android:background="@color/albumPageLight">
<include
android:id="@+id/app_bar_layout"
layout="@layout/sobot_common_layout_titlebar" />
<include layout="@layout/album_content_album" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">SobotAlbum</string>
<string name="sobot_yulan">Preview</string>
<string name="sobot_download">download</string>
<string name="sobot_pic">picture</string>
<!--选择多张图片-->
<string name="sobot_select_photo_num"> %s selected</string>
<string name="sobot_add_photos">Add to attachment</string>
<string name="sobot_album_all_images">Select image</string>
<string name="sobot_album_all_videos">Select video</string>
<string name="sobot_album_all_images_videos">All Images/Videos</string>
<string name="sobot_album_select_images">Select photo</string>
<string name="sobot_album_title">Camera</string>
<string name="sobot_album_check_image_little">Please select an image</string>
<string name="sobot_album_check_video_little">Please select a video</string>
<string name="sobot_album_check_album_little">Please select an image or video</string>
<string name="sobot_pic_siza_xiaoyu">Image size should be less than 50M</string>
<string name="album_loading">Loading&#8230;</string>
<string name="album_converting">Loading&#8230;</string>
<string name="album_thumbnail">Generating thumbnails&#8230;</string>
<string name="album_not_found_image">No image items found</string>
<string name="album_not_found_video">No video items found</string>
<string name="album_not_found_album">No media items found</string>
<string name="album_camera_image_capture">Take Picture</string>
<string name="album_camera_video_capture">Record Video</string>
<string name="album_title">Media Album</string>
<string name="album_all_images">All Images</string>
<string name="album_all_videos">All Videos</string>
<string name="album_all_images_videos">All Images/Videos</string>
<string name="album_ok">OK</string>
<string name="album_confirm">Confirm</string>
<string name="album_cancel">Cancel</string>
<string name="album_check_image_little">Please select a image item.</string>
<string name="album_check_video_little">Please select a video item.</string>
<string name="album_check_album_little">Please select a media item.</string>
<string name="album_menu_finish">Finish</string>
<string name="album_title_permission_failed">Permission denied</string>
<string name="album_permission_storage_failed_hint">No storage permission, please authorize before use.</string>
<string name="album_permission_camera_image_failed_hint">No Camera permission or Storage permission, please authorize before use.</string>
<string name="album_permission_camera_video_failed_hint">No Camera permission, Storage permission or Microphone permission, please authorize it before use.</string>
<plurals name="album_check_image_limit">
<item quantity="one">You can only select %1$d image item.</item>
<item quantity="other">You can only select %1$d image items.</item>
</plurals>
<plurals name="album_check_image_limit_camera">
<item quantity="one">You can only select %1$d image item, can\'t use camera.</item>
<item quantity="other">You can only select %1$d image items, can\'t use camera.</item>
</plurals>
<plurals name="album_check_video_limit">
<item quantity="one">You can only select %1$d video.</item>
<item quantity="other">You can only select %1$d videos.</item>
</plurals>
<plurals name="album_check_video_limit_camera">
<item quantity="one">You can only select %1$d video item, can\'t use camera.</item>
<item quantity="other">You can only select %1$d video items, can\'t use camera.</item>
</plurals>
<plurals name="album_check_album_limit">
<item quantity="one">You can only select %1$d media item.</item>
<item quantity="other">You can only select %1$d media items.</item>
</plurals>
<plurals name="album_check_album_limit_camera">
<item quantity="one">You can only select %1$d media item, can\'t use camera.</item>
<item quantity="other">You can only select %1$d media items, can\'t use camera.</item>
</plurals>
<string name="album_item_unavailable">Unavailable</string>
<string name="album_take_file_unavailable">Unavailable file, please try again.</string>
<string name="btn_function_camera">Camera</string>
<string name="btn_function_album">Pictures and Videos</string>
<string name="btn_function_picture">Only Pictures</string>
<string name="btn_function_video">Only Videos</string>
<string name="btn_function_gallery">Gallery</string>
<string name="btn_function_change_style">Change Style</string>
<string name="btn_function_filter">Filter</string>
<string name="btn_plus">+</string>
<string name="title_camera">Camera</string>
<string name="menu_camera_image">Image</string>
<string name="menu_camera_video">Video</string>
<string name="title_image_select">Select image</string>
<string name="menu_image_select">Image</string>
<string name="title_video_select">Select video</string>
<string name="menu_video_select">Video</string>
<string name="title_album_select">Media Album</string>
<string name="menu_album_select">Album</string>
<string name="title_gallery">Gallery</string>
<string name="menu_preview">Gallery</string>
<string name="btn_gallery_preview">Preview Network Image</string>
<string name="checkable">Checkable Options</string>
<string name="canceled">Canceled</string>
<string name="no_selected">Please select, first.</string>
<string name="hint_image_preview_click">Click on a item to preview them.</string>
<string name="hint_filter_after_visibility">Did you show the filtered files?</string>
<string name="filter_after_visibility_visible">Show</string>
<string name="filter_after_visibility_gone">Hide</string>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 一级文字 颜色 新版 -->
<color name="sobot_wo_new_wenzi_gray1">#161616</color>
<color name="sobot_wo_wenzi_gray1">#161616</color>
<!--工单 主题色 默认绿色 -->
<color name="sobot_wo_theme_color">#09AEB0</color>
<color name="sobot_wo_theme_unable_color">#9909AEB0</color>
<!-- 头部背景 -->
<color name="sobot_color_title_bar_bg">@color/sobot_wo_theme_color</color>
<!-- 二级文字 颜色 新版 -->
<color name="sobot_wo_new_wenzi_gray2">#777474</color>
<color name="sobot_wo_wenzi_gray2">#777474</color>
<!-- 三级文字 颜色 新版-->
<color name="sobot_wo_new_wenzi_gray3">#A3A5A8</color>
<color name="sobot_album_transparent">#00000000</color>
<!--工单客服 白色 -->
<color name="sobot_wo_white_color">#FFFFFF</color>
<color name="albumColorPrimary">#2196F3</color>
<color name="albumColorPrimaryDark">#1E88E5</color>
<color name="albumColorPrimaryBlack">#2B2B2B</color>
<color name="albumPageLight">#FFF</color>
<color name="albumTransparent">#0000</color>
<color name="albumFontHint">#808080</color>
<color name="albumFontLight">#FFF</color>
<color name="albumFontDark">#333</color>
<color name="albumIconDark">#333</color>
<color name="albumLoadingDark">#333</color>
<color name="albumSelectorNormal">#E0E0E0</color>
<color name="albumSheetBottom">#992B2B2B</color>
<color name="albumSheetLayer">#EE2B2B2B</color>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--通用头部 高度-->
<dimen name="sobot_titlebar_height">44dp</dimen>
<!--通用头部 标题大小-->
<dimen name="sobot_titlebar_title_size">16sp</dimen>
<dimen name="sobot_sp_9">9sp</dimen>
<dimen name="sobot_sp_10">10sp</dimen>
<dimen name="sobot_sp_11">11sp</dimen>
<dimen name="sobot_sp_12">12sp</dimen>
<dimen name="sobot_sp_13">13sp</dimen>
<dimen name="sobot_sp_14">14sp</dimen>
<dimen name="sobot_sp_15">15sp</dimen>
<dimen name="sobot_sp_16">16sp</dimen>
<dimen name="sobot_sp_17">17sp</dimen>
<dimen name="sobot_sp_18">18sp</dimen>
<dimen name="sobot_sp_19">19sp</dimen>
<dimen name="sobot_sp_20">20sp</dimen>
<dimen name="sobot_sp_21">21sp</dimen>
<dimen name="sobot_sp_22">22sp</dimen>
<dimen name="sobot_sp_23">23sp</dimen>
<dimen name="sobot_sp_24">24sp</dimen>
<dimen name="sobot_sp_25">25sp</dimen>
<dimen name="sobot_pickerview_textsize">20sp</dimen>
<dimen name="sobot_list_item_padding_top">10dp</dimen>
<dimen name="sobot_setting_item_padding_top">16dp</dimen>
<!--带有删除的编辑输入框-->
<dimen name="sobot_edit_delete_size">35dip</dimen>
<dimen name="sobot_edit_delete_padding">10dip</dimen>
<dimen name="sobot_settingItemView_left_text_width">90dp</dimen>
<dimen name="sobot_settingItemView_left_text_margin_right">10dp</dimen>
<dimen name="sobot_wheelItemTextSize">20sp</dimen>
<dimen name="sobot_wheelIndicatorSize">2dp</dimen>
<dimen name="sobot_wheelItemSpace">8dp</dimen>
<dimen name="sobot_work_order_pic_img">65dp</dimen>
<dimen name="sobot_nomal_margins">15dp</dimen>
<dimen name="sobot_dialog_list_min_h">150dp</dimen>
<dimen name="album_sp_14">14sp</dimen>
<dimen name="album_sp_16">16sp</dimen>
<dimen name="album_sp_18">18sp</dimen>
<dimen name="album_sp_20">20sp</dimen>
<dimen name="album_dp_0">0dp</dimen>
<dimen name="album_dp_1">1dp</dimen>
<dimen name="album_dp_2">2dp</dimen>
<dimen name="album_dp_4">4dp</dimen>
<dimen name="album_dp_6">6dp</dimen>
<dimen name="album_dp_10">10dp</dimen>
<dimen name="album_dp_15">15dp</dimen>
<dimen name="album_dp_20">20dp</dimen>
<dimen name="album_dp_30">30dp</dimen>
<dimen name="album_dp_35">35dp</dimen>
<dimen name="album_dp_40">35dp</dimen>
<dimen name="album_dp_45">45dp</dimen>
<dimen name="album_dp_50">50dp</dimen>
<dimen name="album_dp_80">80dp</dimen>
<dimen name="album_dp_100">100dp</dimen>
<dimen name="album_dp_200">200dp</dimen>
</resources>
<resources>
<string name="app_name">SobotAlbum</string>
<string name="sobot_yulan">预览</string>
<string name="sobot_download">下载</string>
<string name="sobot_pic">图片</string>
<!--选择多张图片-->
<string name="sobot_select_photo_num">已选 %s 个</string>
<string name="sobot_add_photos">添加到附件</string>
<string name="sobot_album_all_images">选择图片</string>
<string name="sobot_album_all_videos">选择视频</string>
<string name="sobot_album_all_images_videos">图片和视频</string>
<string name="sobot_album_select_images">选择照片</string>
<string name="sobot_album_title">相册</string>
<string name="sobot_album_check_image_little">请选择图片</string>
<string name="sobot_album_check_video_little">请选择视频</string>
<string name="sobot_album_check_album_little">请选择图片或视频</string>
<string name="sobot_pic_siza_xiaoyu">图片大小需小于 50M</string>
<string name="album_loading">加载中&#8230;</string>
<string name="album_converting">加载中&#8230;</string>
<string name="album_thumbnail">生成缩略图&#8230;</string>
<string name="album_not_found_image">找不到图片</string>
<string name="album_not_found_video">找不到视频</string>
<string name="album_not_found_album">找不到媒体文件</string>
<string name="album_camera_image_capture">拍照</string>
<string name="album_camera_video_capture">录制视频</string>
<string name="album_title">媒体相册</string>
<string name="album_all_images">所有图片</string>
<string name="album_all_videos">所有视频</string>
<string name="album_all_images_videos">图片和视频</string>
<string name="album_ok">OK</string>
<string name="album_confirm">确定</string>
<string name="album_cancel">取消</string>
<string name="album_check_image_little">请选择图片</string>
<string name="album_check_video_little">请选择视频</string>
<string name="album_check_album_little">请选择图片或视频</string>
<string name="album_menu_finish">完成</string>
<string name="album_title_permission_failed">权限被拒绝</string>
<string name="album_permission_storage_failed_hint">没有存储权限,请在使用前进行授权</string>
<string name="album_permission_camera_image_failed_hint">没有相机权限或存储权限,请在使用前进行授权</string>
<string name="album_permission_camera_video_failed_hint">没有相机权限、存储权限或麦克风权限,请在使用前进行授权</string>
<plurals name="album_check_image_limit">
<item quantity="one">最多可选择 %1$d 张照片</item>
<item quantity="other">最多可选择 %1$d 张照片</item>
</plurals>
<plurals name="album_check_image_limit_camera">
<item quantity="one">您只能选择 %1$d 个图片,不能使用相机。</item>
<item quantity="other">您只能选择 %1$d 个图片,不能使用相机。</item>
</plurals>
<plurals name="album_check_video_limit">
<item quantity="one">您只能选择 %1$d 个视频。</item>
<item quantity="other">您只能选择 %1$d 个视频。</item>
</plurals>
<plurals name="album_check_video_limit_camera">
<item quantity="one">您只能选择 %1$d 个视频,不能使用相机。</item>
<item quantity="other">您只能选择 %1$d 个视频,不能使用相机。</item>
</plurals>
<plurals name="album_check_album_limit">
<item quantity="one">最多可选择 %1$d 张照片</item>
<item quantity="other">最多可选择 %1$d 张照片</item>
</plurals>
<plurals name="album_check_album_limit_camera">
<item quantity="one">您只能选择%1$d个媒体项目,不能使用相机</item>
<item quantity="other">您只能选择%1$d个媒体项目,不能使用相机</item>
</plurals>
<string name="album_item_unavailable">不可用</string>
<string name="album_take_file_unavailable">文件不可用,请重试</string>
<string name="btn_function_camera">相机</string>
<string name="btn_function_album">图片和视频</string>
<string name="btn_function_picture">仅图片</string>
<string name="btn_function_video">仅视频</string>
<string name="btn_function_gallery">图库</string>
<string name="btn_function_change_style">更改样式</string>
<string name="btn_function_filter">过滤器</string>
<string name="btn_plus">+</string>
<string name="title_camera">相机</string>
<string name="menu_camera_image">图像</string>
<string name="menu_camera_video">视频</string>
<string name="title_image_select">选择图像</string>
<string name="menu_image_select">图像</string>
<string name="title_video_select">选择视频</string>
<string name="menu_video_select">视频</string>
<string name="title_album_select">媒体相册</string>
<string name="menu_album_select">专辑</string>
<string name="title_gallery">图库</string>
<string name="menu_preview">图库</string>
<string name="btn_gallery_preview">预览网络图像</string>
<string name="checkable">可检查选项</string>
<string name="canceled">已取消</string>
<string name="no_selected">请先选择</string>
<string name="hint_image_preview_click">单击项目以预览它们</string>
<string name="hint_filter_after_visibility">是否显示了筛选的文件</string>
<string name="filter_after_visibility_visible">显示</string>
<string name="filter_after_visibility_gone">隐藏</string>
</resources>
\ No newline at end of file
<resources>
<style name="Album" />
<style name="Album.Theme" />
<style name="Album.MatchParent">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
</style>
<style name="Album.MatchParent.SheetBottom">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/album_dp_45</item>
</style>
<style name="Album.WrapContent">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="Album.WrapContent.WidthMatchParent">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="Album.WrapContent.Transfer">
<item name="android:clickable">true</item>
<item name="android:paddingTop">@dimen/album_dp_4</item>
<item name="android:paddingRight">@dimen/album_dp_4</item>
<item name="android:paddingBottom">@dimen/album_dp_10</item>
<item name="android:paddingLeft">@dimen/album_dp_10</item>
</style>
<style name="Album.AppBar">
<item name="android:background">@color/albumTransparent</item>
</style>
<style name="Album.AppBar.General">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="Album.Theme.Toolbar" />
<style name="Album.Item" />
<style name="MatchParent">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
</style>
<style name="Loading">
<item name="android:minWidth">@dimen/album_dp_40</item>
<item name="android:maxWidth">@dimen/album_dp_40</item>
<item name="android:minHeight">@dimen/album_dp_40</item>
<item name="android:maxHeight">@dimen/album_dp_40</item>
<item name="android:layout_width">@dimen/album_dp_40</item>
<item name="android:layout_height">@dimen/album_dp_40</item>
</style>
<style name="WrapContent">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="Album.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:backgroundDimEnabled">true</item>
</style>
<style name="Album.Dialog.Folder">
<item name="android:windowBackground">@color/albumTransparent</item>
<item name="android:windowIsFloating">false</item>
<item name="android:windowFullscreen">true</item>
</style>
<style name="SheetBottom">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_centerVertical">true</item>
<item name="android:ellipsize">end</item>
<item name="android:maxLines">1</item>
<item name="android:maxWidth">@dimen/album_dp_200</item>
<item name="android:textColor">@color/albumFontLight</item>
</style>
<style name="Toolbar">
<item name="android:theme">@style/Album.Theme.Toolbar</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">90dp</item>
</style>
<style name="Button">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/album_dp_80</item>
<item name="android:textSize">@dimen/album_sp_20</item>
<item name="android:textColor">@color/albumFontLight</item>
<item name="android:layout_margin">@dimen/album_dp_1</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Yan Zhenjie.
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.
-->
<paths>
<external-path
name="external_path"
path="."/>
<external-files-path
name="external_files_path"
path="."/>
<external-cache-path
name="external_cache_path"
path="."/>
<files-path
name="file_path"
path="."/>
<cache-path
name="cache_path"
path="."/>
</paths>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment