diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6480dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +.DS_Store +/build +/captures +.externalNativeBuild +/app/google-services.json +/.idea/ diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..58146b3 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,90 @@ +apply plugin: 'com.android.application' +apply plugin: "com.neenbedankt.android-apt" + +android { + compileSdkVersion 25 + buildToolsVersion "24.0.2" + defaultConfig { + applicationId "com.styleme.floating.toolbox.pro" + minSdkVersion 18 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + sourceSets { + main { + res.srcDirs = [ + "src/main/res/", + "src/main/res/layouts/main_layouts", + "src/main/res/layouts/row_layouts", + "src/main/res/layouts/other_layouts", + "src/main/res/layouts/floating_layouts", + "src/main/res" + ] + } + } + lintOptions { + quiet true + abortOnError true + htmlReport true + xmlReport true + fatal 'NewApi' + disable 'InvalidPackage' + } +} +repositories { + maven { url "https://clojars.org/repo/" } + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven { url "http://dl.bintray.com/amulyakhare/maven" } + maven { url "https://jitpack.io" } +} +dependencies { + ext { + supportVerion = "25.0.0" + firebase = "9.8.00" + } + compile fileTree(include: ['*.jar'], dir: 'libs') + compile "com.android.support:appcompat-v7:${supportVerion}" + compile "com.android.support:design:${supportVerion}" + compile "com.android.support:cardview-v7:${supportVerion}" + compile "com.android.support:recyclerview-v7:${supportVerion}" + compile "com.android.support:palette-v7:${supportVerion}" + compile "com.android.support:preference-v14:${supportVerion}" + compile "com.android.support:customtabs:${supportVerion}" + compile('com.github.ozodrukh:CircularReveal:2.0.1@aar') { + transitive = true; + } + compile('com.mikepenz:aboutlibraries:5.8.1@aar') { + transitive = true + } + compile project(':color-picker') + compile "com.google.firebase:firebase-messaging:${firebase}" + compile "com.google.firebase:firebase-analytics:${firebase}" + compile "com.google.firebase:firebase-database:${firebase}" + compile "com.google.firebase:firebase-auth:${firebase}" + compile "com.google.android.gms:play-services-auth:${firebase}" + compile 'com.google.code.gson:gson:2.7' + compile 'com.jakewharton:butterknife:8.4.0' + compile 'org.greenrobot:eventbus:3.0.0' + compile 'cat.ereza:customactivityoncrash:1.5.0' + compile 'frankiesardo:icepick:3.1.0' + compile 'com.mikhaellopez:circularfillableloaders:1.2.0' + compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' + compile 'com.github.pluscubed:recycler-fast-scroll:0.3.2@aar' + compile 'com.github.satyan:sugar:1.4' + compile 'it.sephiroth.android.library.bottomnavigation:bottom-navigation:1.0.7' + compile 'com.github.vihtarb:tooltip:0.1.8' + compile 'com.github.nisrulz:sensey:1.5.0' + compile 'org.adw.library:discrete-seekbar:1.0.1' + compile 'com.theartofdev.edmodo:android-image-cropper:2.3.1' + compile 'pub.devrel:easypermissions:0.2.1' + apt 'frankiesardo:icepick-processor:3.1.0' + apt 'com.jakewharton:butterknife-compiler:8.4.0' +} +apply plugin: "com.google.gms.google-services" \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..65fe9b3 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/Kosh/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..8b4c9f7 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/fonts/app_font.ttf b/app/src/main/assets/fonts/app_font.ttf new file mode 100644 index 0000000..b158a33 Binary files /dev/null and b/app/src/main/assets/fonts/app_font.ttf differ diff --git a/app/src/main/java/com/fastaccess/App.java b/app/src/main/java/com/fastaccess/App.java new file mode 100644 index 0000000..f6b8f1f --- /dev/null +++ b/app/src/main/java/com/fastaccess/App.java @@ -0,0 +1,68 @@ +package com.fastaccess; + +import android.app.Application; +import android.support.annotation.NonNull; +import android.support.v7.preference.PreferenceManager; + +import com.fastaccess.helper.FileHelper; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.provider.icon.IconCache; +import com.fastaccess.ui.modules.main.MainView; +import com.google.firebase.analytics.FirebaseAnalytics; +import com.google.firebase.database.FirebaseDatabase; +import com.orm.SugarContext; + +import cat.ereza.customactivityoncrash.CustomActivityOnCrash; + +/** + * Created by Kosh on 24 May 2016, 7:51 PM + */ + +public class App extends Application { + + private static App instance; + private FirebaseAnalytics firebaseAnalytics; + private IconCache iconCache; + + @Override public void onCreate() { + super.onCreate(); + instance = this; + SugarContext.init(this.getApplicationContext()); + FileHelper.initFolderName(getString(R.string.app_name)); + PrefHelper.init(this.getApplicationContext()); + TypeFaceHelper.generateTypeface(this.getApplicationContext()); + PreferenceManager.setDefaultValues(this, R.xml.fa_settings, false); + if (!BuildConfig.DEBUG) { + CustomActivityOnCrash.setRestartActivityClass(MainView.class); + CustomActivityOnCrash.setShowErrorDetails(BuildConfig.DEBUG); + CustomActivityOnCrash.install(this); + } + FirebaseDatabase.getInstance().setPersistenceEnabled(false); + } + + @NonNull public static App getInstance() { + return instance; + } + + @NonNull public FirebaseAnalytics getFirebaseAnalytics() { + if (firebaseAnalytics == null) { + firebaseAnalytics = FirebaseAnalytics.getInstance(this); + } + return firebaseAnalytics; + } + + @NonNull public IconCache getIconCache() { + if (iconCache == null) { + iconCache = new IconCache(this.getApplicationContext()); + } + return iconCache; + } + + public void flushIconPack() { + if (iconCache != null) { + iconCache.flush(); + iconCache = null; + } + } +} diff --git a/app/src/main/java/com/fastaccess/data/dao/AppsModel.java b/app/src/main/java/com/fastaccess/data/dao/AppsModel.java new file mode 100644 index 0000000..646e78d --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/AppsModel.java @@ -0,0 +1,181 @@ +package com.fastaccess.data.dao; + +import android.content.ComponentName; +import android.graphics.Bitmap; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +import com.google.firebase.database.Exclude; +import com.orm.SugarRecord; +import com.orm.dsl.Ignore; +import com.orm.query.Condition; +import com.orm.query.Select; +import com.orm.util.NamingHelper; + +import java.util.Comparator; +import java.util.List; + +/** + * Created by Kosh on 10 Oct 2016, 10:23 PM + */ + +public class AppsModel extends SugarRecord implements Parcelable { + private long folderId; + private String appName; + private String packageName; + private String iconPath; + private String activityInfoName; + private int indexPosition; + private int countEntry; + @Exclude @Ignore private Bitmap bitmap; + + public AppsModel() {}//default constructor. + + public long getFolderId() { + return folderId; + } + + public void setFolderId(long folderId) { + this.folderId = folderId; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public String getIconPath() { + return iconPath; + } + + public void setIconPath(String iconPath) { + this.iconPath = iconPath; + } + + public String getActivityInfoName() { + return activityInfoName; + } + + public void setActivityInfoName(String activityInfoName) { + this.activityInfoName = activityInfoName; + } + + public int getIndexPosition() { + return indexPosition; + } + + public void setIndexPosition(int indexPosition) { + this.indexPosition = indexPosition; + } + + public int getCountEntry() { + return countEntry; + } + + public void setCountEntry(int countEntry) { + this.countEntry = countEntry; + } + + public String getAppName() { + return appName; + } + + public void setAppName(String appName) { + this.appName = appName; + } + + @Exclude public ComponentName getComponentName() { + return new ComponentName(packageName, activityInfoName); + } + + public void setBitmap(Bitmap bitmap) { + this.bitmap = bitmap; + } + + public Bitmap getBitmap() { + return bitmap; + } + + public static List getApps() { + return Select.from(AppsModel.class) + .where(Condition.prop(NamingHelper.toSQLNameDefault("folderId")).eq(0)) + .orderBy(NamingHelper.toSQLNameDefault("indexPosition") + " ASC") + .list(); + } + + public static List getApps(long folderId) { + return Select.from(AppsModel.class) + .where(Condition.prop(NamingHelper.toSQLNameDefault("folderId")).eq(folderId)) + .list(); + } + + public static int lastPosition() { + AppsModel appsModel = Select.from(AppsModel.class).orderBy(NamingHelper.toSQLNameDefault("indexPosition") + " DESC").first(); + if (appsModel != null) { + return appsModel.getIndexPosition(); + } + return 0; + } + + public static long countApps(long folderId) { + return count(AppsModel.class, NamingHelper.toSQLNameDefault("folderId") + " = ?", new String[]{String.valueOf(folderId)}); + } + + public static void deleteAllByFolder(@NonNull FolderModel folder) { + deleteAll(AppsModel.class, NamingHelper.toSQLNameDefault("folderId") + " = ?", String.valueOf(folder.getId())); + } + + public static void deleteAll() { + deleteAll(AppsModel.class); + } + + public static boolean exists(@NonNull String activityInfoName, @NonNull String packageName) { + return Select.from(AppsModel.class) + .where(Condition.prop(NamingHelper.toSQLNameDefault("activityInfoName")).eq(activityInfoName)) + .and(Condition.prop(NamingHelper.toSQLNameDefault("packageName")).eq(packageName)) + .and(Condition.prop(NamingHelper.toSQLNameDefault("folderId")).eq(0)) + .first() != null; + } + + public static Comparator sortApps() { + return new Comparator() { + @Override public int compare(AppsModel one, AppsModel two) { + return one.getAppName().compareTo(two.getAppName()); + } + }; + } + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(this.folderId); + dest.writeString(this.appName); + dest.writeString(this.packageName); + dest.writeString(this.iconPath); + dest.writeString(this.activityInfoName); + dest.writeInt(this.indexPosition); + dest.writeInt(this.countEntry); + dest.writeLong(this.getId()); + } + + protected AppsModel(Parcel in) { + this.folderId = in.readLong(); + this.appName = in.readString(); + this.packageName = in.readString(); + this.iconPath = in.readString(); + this.activityInfoName = in.readString(); + this.indexPosition = in.readInt(); + this.countEntry = in.readInt(); + this.setId((Long) in.readValue(Long.class.getClassLoader())); + } + + public static final Creator CREATOR = new Creator() { + @Override public AppsModel createFromParcel(Parcel source) {return new AppsModel(source);} + + @Override public AppsModel[] newArray(int size) {return new AppsModel[size];} + }; +} diff --git a/app/src/main/java/com/fastaccess/data/dao/BackupRestoreModel.java b/app/src/main/java/com/fastaccess/data/dao/BackupRestoreModel.java new file mode 100644 index 0000000..3b36f37 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/BackupRestoreModel.java @@ -0,0 +1,98 @@ +package com.fastaccess.data.dao; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; + +import org.greenrobot.eventbus.EventBus; + +import java.util.List; +import java.util.Map; + +/** + * Created by Kosh on 23 Oct 2016, 8:51 PM + */ + +public class BackupRestoreModel { + + private String uid; + private List folders; + private List appsModels; + private Map settings; + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public List getFolders() { + return folders; + } + + public void setFolders(List folders) { + this.folders = folders; + } + + public List getAppsModels() { + return appsModels; + } + + public void setAppsModels(List appsModels) { + this.appsModels = appsModels; + } + + public Map getSettings() { + return settings; + } + + public void setSettings(Map settings) { + this.settings = settings; + } + + @Nullable public static BackupRestoreModel backup() { + BackupRestoreModel model = new BackupRestoreModel(); + FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser(); + if (firebaseUser == null) return null; + model.setUid(firebaseUser.getUid()); + model.setFolders(FolderModel.getFolders()); + model.setSettings(PrefHelper.getAll()); + model.setAppsModels(AppsModel.getApps()); + return model; + } + + public static void restore(@NonNull BackupRestoreModel model) { + if (model.getFolders() != null) { + FolderModel.saveInTx(model.getFolders()); + EventBus.getDefault().post(new FolderEventModel()); + } + if (model.getAppsModels() != null) { + AppsModel.saveInTx(model.getAppsModels()); + EventBus.getDefault().post(new FloatingEventModel()); + } + if (model.getSettings() != null) { + for (String key : model.getSettings().keySet()) { + if (key != null && !key.equalsIgnoreCase("null")) { + if (key.equalsIgnoreCase(PrefConstant.FA_BACKGROUND)) { + PrefHelper.set(key, Integer.valueOf(model.getSettings().get(key).toString())); + } else { + PrefHelper.set(key, model.getSettings().get(key)); + } + } + } + } + } + + @Override public String toString() { + return "BackupRestoreModel{" + + "uid='" + uid + '\'' + + ", settings='" + settings + '\'' + + '}'; + } +} diff --git a/app/src/main/java/com/fastaccess/data/dao/DeviceAppsEventModel.java b/app/src/main/java/com/fastaccess/data/dao/DeviceAppsEventModel.java new file mode 100644 index 0000000..3db99e8 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/DeviceAppsEventModel.java @@ -0,0 +1,10 @@ +package com.fastaccess.data.dao; + +/** + * Created by Kosh on 16 Oct 2016, 7:30 PM + */ + +public class DeviceAppsEventModel { + + public DeviceAppsEventModel() {} +} diff --git a/app/src/main/java/com/fastaccess/data/dao/FloatingEventModel.java b/app/src/main/java/com/fastaccess/data/dao/FloatingEventModel.java new file mode 100644 index 0000000..942ae31 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/FloatingEventModel.java @@ -0,0 +1,30 @@ +package com.fastaccess.data.dao; + +/** + * Created by Kosh on 15 Oct 2016, 8:57 PM + */ + +public class FloatingEventModel { + + private boolean settingsChanged; + private String key; + + public FloatingEventModel() {} + + public FloatingEventModel(boolean settingsChanged) { + this.settingsChanged = settingsChanged; + } + + public FloatingEventModel(boolean settingsChanged, String key) { + this.settingsChanged = settingsChanged; + this.key = key; + } + + public boolean isSettingsChanged() { + return settingsChanged; + } + + public String getKey() { + return key; + } +} diff --git a/app/src/main/java/com/fastaccess/data/dao/FolderEventModel.java b/app/src/main/java/com/fastaccess/data/dao/FolderEventModel.java new file mode 100644 index 0000000..85c9cb5 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/FolderEventModel.java @@ -0,0 +1,9 @@ +package com.fastaccess.data.dao; + +/** + * Created by Kosh on 22 Oct 2016, 11:28 AM + */ + +public class FolderEventModel { + public FolderEventModel() {} +} diff --git a/app/src/main/java/com/fastaccess/data/dao/FolderModel.java b/app/src/main/java/com/fastaccess/data/dao/FolderModel.java new file mode 100644 index 0000000..bc99cf4 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/FolderModel.java @@ -0,0 +1,119 @@ +package com.fastaccess.data.dao; + +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + +import com.orm.SugarRecord; +import com.orm.dsl.Ignore; +import com.orm.dsl.Unique; +import com.orm.query.Select; +import com.orm.util.NamingHelper; + +import java.util.List; + +/** + * Created by Kosh on 10 Oct 2016, 10:21 PM + */ + +public class FolderModel extends SugarRecord implements Parcelable { + + @Unique private String folderName; + private long createdDate; + private int orderIndex; + private int color; + private int appsCount; + @Ignore private List folderApps; + + public String getFolderName() { + return folderName; + } + + public void setFolderName(String folderName) { + this.folderName = folderName; + } + + public long getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(long createdDate) { + this.createdDate = createdDate; + } + + public int getOrderIndex() { + return orderIndex; + } + + public void setOrderIndex(int orderIndex) { + this.orderIndex = orderIndex; + } + + public int getColor() { + return color; + } + + public void setColor(int color) { + this.color = color; + } + + public int getAppsCount() { + appsCount = (int) AppsModel.countApps(getId()); + return appsCount; + } + + public static List getFolders() { + return Select.from(FolderModel.class) + .orderBy(NamingHelper.toSQLNameDefault("createdDate") + " DESC") + .list(); + } + + public static void deleteFolder(@NonNull FolderModel model) { + model.delete(); + AppsModel.deleteAll(AppsModel.class, NamingHelper.toSQLNameDefault("folderId") + " = ?", String.valueOf(model.getId())); + } + + public FolderModel() {} + + @Override public int describeContents() { return 0; } + + @Override public void writeToParcel(Parcel dest, int flags) { + dest.writeString(this.folderName); + dest.writeLong(this.createdDate); + dest.writeInt(this.orderIndex); + dest.writeInt(this.color); + dest.writeInt(this.appsCount); + dest.writeValue(this.getId()); + } + + protected FolderModel(Parcel in) { + this.folderName = in.readString(); + this.createdDate = in.readLong(); + this.orderIndex = in.readInt(); + this.color = in.readInt(); + this.appsCount = in.readInt(); + this.setId((Long) in.readValue(Long.class.getClassLoader())); + } + + public static final Creator CREATOR = new Creator() { + @Override public FolderModel createFromParcel(Parcel source) {return new FolderModel(source);} + + @Override public FolderModel[] newArray(int size) {return new FolderModel[size];} + }; + + public List getFolderApps() { + return AppsModel.getApps(getId()); + } + + public void setFolderApps(List folderApps) { + this.folderApps = folderApps; + if (folderApps != null && !folderApps.isEmpty()) { + for (AppsModel app : folderApps) { + if (app.getFolderId() == 0) { + app.setFolderId(getId()); + } + if (app.getFolderId() != 0) app.save(); + } + } + } +} diff --git a/app/src/main/java/com/fastaccess/data/dao/SelectedAppsEventModel.java b/app/src/main/java/com/fastaccess/data/dao/SelectedAppsEventModel.java new file mode 100644 index 0000000..70ea086 --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/SelectedAppsEventModel.java @@ -0,0 +1,10 @@ +package com.fastaccess.data.dao; + +/** + * Created by Kosh on 12 Oct 2016, 7:24 PM + */ + +public class SelectedAppsEventModel { + + public SelectedAppsEventModel() {} +} diff --git a/app/src/main/java/com/fastaccess/data/dao/ThemePackEventModel.java b/app/src/main/java/com/fastaccess/data/dao/ThemePackEventModel.java new file mode 100644 index 0000000..9bb3cbb --- /dev/null +++ b/app/src/main/java/com/fastaccess/data/dao/ThemePackEventModel.java @@ -0,0 +1,10 @@ +package com.fastaccess.data.dao; + +/** + * Created by Kosh on 17 Oct 2016, 7:48 PM + */ + +public class ThemePackEventModel { + + public ThemePackEventModel() {} +} diff --git a/app/src/main/java/com/fastaccess/helper/ActivityHelper.java b/app/src/main/java/com/fastaccess/helper/ActivityHelper.java new file mode 100644 index 0000000..f2462fd --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/ActivityHelper.java @@ -0,0 +1,92 @@ +package com.fastaccess.helper; + +import android.app.Activity; +import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.net.Uri; +import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.customtabs.CustomTabsIntent; +import android.support.v4.app.Fragment; + +import com.fastaccess.R; +import com.mikepenz.aboutlibraries.Libs; +import com.mikepenz.aboutlibraries.LibsBuilder; + +import java.io.File; + +/** + * Created by Kosh on 12/12/15 10:51 PM + */ +public class ActivityHelper { + + public static final int REQUEST_CODE = 100; + public static final int CAMERA_REQUEST_CODE = 101; + public static final int SELECT_PHOTO_REQUEST = 102; + + @Nullable public static Activity getActivity(@Nullable Context cont) { + if (cont == null) return null; + else if (cont instanceof Activity) return (Activity) cont; + else if (cont instanceof ContextWrapper) return getActivity(((ContextWrapper) cont).getBaseContext()); + return null; + } + + public static int getScreenOrientation(Context activity) { + return activity.getResources().getConfiguration().orientation; + } + + public static void startActivity(@NonNull Context context, Class className) { + Intent intent = new Intent(context, className); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivity(intent); + } + + public static void startActivityWithFinish(@NonNull Activity context, Class className) { + Intent intent = new Intent(context, className); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + context.startActivity(intent); + } + + public static void startGalleryIntent(@NonNull Activity activity) { + Intent intent = new Intent(Intent.ACTION_PICK); + intent.setType("image/*"); + activity.startActivityForResult(intent, SELECT_PHOTO_REQUEST); + } + + public static void startGalleryIntent(@NonNull Fragment fragment) { + Intent intent = new Intent(Intent.ACTION_PICK); + intent.setType("image/*"); + fragment.startActivityForResult(intent, SELECT_PHOTO_REQUEST); + } + public static void startCameraIntent(@NonNull Activity activity, @NonNull File file) { + Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) { + takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); + activity.startActivityForResult(takePictureIntent, CAMERA_REQUEST_CODE); + } + } + + public static void startCustomTab(@NonNull Activity context) { + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); + builder.setToolbarColor(ViewHelper.getPrimaryColor(context)); + CustomTabsIntent tabsIntent = builder.build(); + tabsIntent.launchUrl(context, Uri.parse("https://github.com/k0shk0sh/NewKam"));//TODO change url + } + + public static void startLibs(@NonNull Activity activity) { + new LibsBuilder() + .withFields(R.string.class.getFields()) + .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) + .withActivityTheme(R.style.AppTheme) + .withAboutIconShown(true) + .withAboutVersionShown(true) + .withAutoDetect(true) + .withLicenseShown(true) + .withVersionShown(true) + .withActivityTitle(activity.getString(R.string.libs)) + .start(activity); + } + +} diff --git a/app/src/main/java/com/fastaccess/helper/AnimHelper.java b/app/src/main/java/com/fastaccess/helper/AnimHelper.java new file mode 100644 index 0000000..50a7124 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/AnimHelper.java @@ -0,0 +1,157 @@ +package com.fastaccess.helper; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.graphics.Rect; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.UiThread; +import android.support.v4.view.ViewCompat; +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.ViewTreeObserver; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.Interpolator; +import android.view.animation.LinearInterpolator; + +import java.util.Arrays; +import java.util.List; + +import io.codetail.animation.ViewAnimationUtils; + +/** + * Created by Kosh on 27 May 2016, 9:04 PM + */ + +public class AnimHelper { + + public interface AnimationCallback { + void onAnimationEnd(); + + void onAnimationStart(); + } + + private static final Interpolator interpolator = new LinearInterpolator(); + + @UiThread public static void animateVisibility(@Nullable final View view, final boolean show) { + animateVisibility(view, show, null); + } + + @UiThread public static void animateVisibility(@Nullable final View view, final boolean show, @Nullable final AnimationCallback callback) { + if (view == null) { + return; + } + if (!ViewCompat.isAttachedToWindow(view)) { + view.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override public boolean onPreDraw() { + view.getViewTreeObserver().removeOnPreDrawListener(this); + animateSafeVisibility(show, view, callback); + return true; + } + }); + } else { + animateSafeVisibility(show, view, callback); + } + } + + @UiThread private static void animateSafeVisibility(final boolean show, @NonNull final View view, @Nullable final AnimationCallback callback) { + view.clearAnimation(); + if (view.getAnimation() != null) view.getAnimation().cancel(); + ViewPropertyAnimator animator = view.animate().alpha(show ? 1F : 0F).setInterpolator(new AccelerateInterpolator()) + .setListener(new AnimatorListenerAdapter() { + @Override public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + if (callback != null) callback.onAnimationStart(); + if (show) { + view.setScaleX(1); + view.setScaleY(1); + view.setVisibility(View.VISIBLE); + } + } + + @Override public void onAnimationEnd(@NonNull Animator animation) { + super.onAnimationEnd(animation); + if (!show) { + view.setVisibility(View.GONE); + view.setScaleX(0); + view.setScaleY(0); + } + if (callback != null) callback.onAnimationEnd(); + animation.removeListener(this); + view.clearAnimation(); + } + }); + animator.scaleX(show ? 1 : 0).scaleY(show ? 1 : 0); + } + + @UiThread @NonNull private static List getBeats(@NonNull View view) { + ObjectAnimator[] animator = new ObjectAnimator[]{ + ObjectAnimator.ofFloat(view, "scaleY", 1, 1.1f, 1), + ObjectAnimator.ofFloat(view, "scaleX", 1, 1.1f, 1) + }; + return Arrays.asList(animator); + } + + @UiThread public static void startBeatsAnimation(@NonNull View view) { + view.clearAnimation(); + if (view.getAnimation() != null) { + view.getAnimation().cancel(); + } + List animators = getBeats(view); + for (ObjectAnimator anim : animators) { + anim.setDuration(300).start(); + anim.setInterpolator(interpolator); + } + } + + @UiThread public static void circularReveal(final View mRevealView, final View from, final boolean show) { + if (ViewCompat.isAttachedToWindow(mRevealView)) { + if (show) { + if (mRevealView.isShown()) return; + } else { + if (!mRevealView.isShown()) { + return; + } + } + reveal(mRevealView, show, from); + } else { + mRevealView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override public boolean onPreDraw() { + mRevealView.getViewTreeObserver().removeOnPreDrawListener(this); + if (show) { + if (mRevealView.isShown()) return true; + } else { + if (!mRevealView.isShown()) { + return true; + } + } + reveal(mRevealView, show, from); + return true; + } + }); + } + } + + @UiThread private static void reveal(final View mRevealView, final boolean show, View from) { + Rect rect = ViewHelper.getLayoutPosition(from); + int x = (int) rect.exactCenterX(); + int y = (int) rect.exactCenterY(); + Animator animator = ViewAnimationUtils.createCircularReveal(mRevealView, x, y, 0, Math.max(rect.width(), rect.height())); + animator.setDuration(400L); + animator.setInterpolator(new AccelerateDecelerateInterpolator()); + mRevealView.setVisibility(View.VISIBLE); + if (!show) { + animator.addListener(new AnimatorListenerAdapter() { + @Override public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + mRevealView.setVisibility(View.GONE); + animation.removeListener(this); + } + }); + animator.start(); + } + } + +} diff --git a/app/src/main/java/com/fastaccess/helper/AppHelper.java b/app/src/main/java/com/fastaccess/helper/AppHelper.java new file mode 100644 index 0000000..5713061 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/AppHelper.java @@ -0,0 +1,276 @@ +package com.fastaccess.helper; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.location.LocationManager; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkInfo; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.support.annotation.ColorRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; + +import com.fastaccess.BuildConfig; +import com.fastaccess.data.dao.AppsModel; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by kosh20111 on 18 Oct 2016, 9:29 PM + */ + +public class AppHelper { + + private static final int GPS_REQUEST_CODE = 2004; + + public static boolean isOnline(Context context) { + boolean haveConnectedWifi = false; + boolean haveConnectedMobile = false; + try { + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (isM()) { + Network networks = cm.getActiveNetwork(); + NetworkInfo netInfo = cm.getNetworkInfo(networks); + haveConnectedWifi = netInfo.getType() == ConnectivityManager.TYPE_WIFI && netInfo.getState().equals(NetworkInfo.State.CONNECTED); + haveConnectedMobile = netInfo.getType() == ConnectivityManager.TYPE_MOBILE && netInfo.getState().equals(NetworkInfo.State.CONNECTED); + } else { + NetworkInfo[] netInfo = cm.getAllNetworkInfo(); + for (NetworkInfo ni : netInfo) { + if (ni.getTypeName().equalsIgnoreCase("WIFI")) { + if (ni.isConnected()) + haveConnectedWifi = true; + } + if (ni.getTypeName().equalsIgnoreCase("MOBILE")) { + if (ni.isConnected()) + haveConnectedMobile = true; + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return haveConnectedWifi || haveConnectedMobile; + } + + public static boolean isApplicationInstalled(Context context, String packageName) { + PackageInfo info = null; + try { + info = context.getPackageManager().getPackageInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return info != null; + } + + public static boolean isM() {return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;} + + public static boolean isLollipopOrHigher() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + } + + public static boolean isBelowLollipop() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; + } + + public static boolean isGPSEnabled(Context context) { + LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); + return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + } + + public static void turnGpsOn(Activity context) { + context.startActivityForResult(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS), GPS_REQUEST_CODE); + } + + public static void setStatusBarColor(Activity activity, @ColorRes int colorRes) { + if (isLollipopOrHigher()) { + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + activity.getWindow().setStatusBarColor(ActivityCompat.getColor(activity, colorRes)); + } + } + + public static String getTransitionName(@NonNull String defaultValue, @NonNull View view) { + if (isLollipopOrHigher()) { + return !InputHelper.isEmpty(view.getTransitionName()) ? view.getTransitionName() : defaultValue; + } + return defaultValue; + } + + public static boolean isLandscape(int orientation) { + return (orientation == Configuration.ORIENTATION_LANDSCAPE); + } + + public static boolean hasNavigationBar() { + return KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK) && KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_HOME); + + } + + public static int getNavigationBarHeight(Context context) { + int orientation = context.getResources().getConfiguration().orientation; + Resources resources = context.getResources(); + int id = resources.getIdentifier( + orientation == Configuration.ORIENTATION_PORTRAIT ? "navigation_bar_height" : "navigation_bar_height_landscape", "dimen", "android"); + if (id > 0) { + return resources.getDimensionPixelSize(id); + } + return 0; + } + + public static void shareApp(@NonNull Context context) { + final String appPackageName = context.getPackageName(); + try { + context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName))); + } catch (Exception e) { + context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName))); + } + } + + @NonNull public static List getInstalledPackages(@NonNull Context context) { + final PackageManager pm = context.getPackageManager(); + Process process; + List result = new ArrayList<>(); + BufferedReader bufferedReader = null; + try { + process = Runtime.getRuntime().exec("pm list packages"); + bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); + String line; + while ((line = bufferedReader.readLine()) != null) { + final String packageName = line.substring(line.indexOf(':') + 1); + PackageInfo packageInfo = pm.getPackageInfo(packageName, 0); + Intent mainIntent = pm.getLaunchIntentForPackage(packageInfo.applicationInfo.packageName); + if (mainIntent != null) { + ResolveInfo resolveInfo = pm.resolveActivity(mainIntent, 0); + if (resolveInfo != null) { + if (!packageName.equalsIgnoreCase(BuildConfig.APPLICATION_ID)) { + AppsModel model = new AppsModel(); + model.setPackageName(resolveInfo.activityInfo.applicationInfo.packageName); + model.setActivityInfoName(resolveInfo.activityInfo.name); + model.setAppName(resolveInfo.loadLabel(pm).toString()); + result.add(model); + } + } + } + } + process.waitFor(); + Collections.sort(result, AppsModel.sortApps()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (bufferedReader != null) + try { + bufferedReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return result; + } + + public static void showKeyboard(@NonNull View v) { + showKeyboard(v, v.getContext()); + } + + public static void hideKeyboard(@NonNull View view) { + hideKeyboard(view, view.getContext()); + } + + public static void showKeyboard(@NonNull View v, @NonNull Context activity) { + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(v, 0); + } + + public static void hideKeyboard(@NonNull View view, @NonNull Context activity) { + InputMethodManager inputManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + @Nullable public static Fragment getFragmentByTag(@NonNull FragmentManager fragmentManager, @NonNull String tag) { + return fragmentManager.findFragmentByTag(tag); + } + + @Nullable public static Fragment getVisibleFragment(@NonNull FragmentManager manager) { + List fragments = manager.getFragments(); + if (fragments != null && !fragments.isEmpty()) { + for (Fragment fragment : fragments) { + if (fragment != null && fragment.isVisible()) { + Logger.e(fragment.getClass().getSimpleName(), fragment.isVisible()); + return fragment; + } + } + } + return null; + } + + @Nullable public static String saveBitmap(Bitmap image) { + try { + File file = FileHelper.generateFile("fa_image_icon"); + if (file.exists()) { + file.delete(); + } + OutputStream fOut = new FileOutputStream(file); + image.compress(Bitmap.CompressFormat.PNG, 70, fOut); + fOut.flush(); + fOut.close(); + return file.getAbsolutePath(); + } catch (Exception e) { + return null; + } + } + + @Nullable public static Bitmap getBitmapFromUri(@NonNull Uri uri, @NonNull Context context) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + options.inSampleSize = 5; + try { + BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri), null, options); + options.inSampleSize = calculateInSampleSize(options, 300, 300); + options.inJustDecodeBounds = false; + return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri), null, options); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { + final int height = options.outHeight; + final int width = options.outWidth; + int inSampleSize = 1; + if (height > reqHeight || width > reqWidth) { + final int halfHeight = height / 2; + final int halfWidth = width / 2; + while ((halfHeight / inSampleSize) >= reqHeight + && (halfWidth / inSampleSize) >= reqWidth) { + inSampleSize *= 2; + } + } + return inSampleSize; + } +} diff --git a/app/src/main/java/com/fastaccess/helper/Bundler.java b/app/src/main/java/com/fastaccess/helper/Bundler.java new file mode 100644 index 0000000..9cbeddf --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/Bundler.java @@ -0,0 +1,195 @@ +package com.fastaccess.helper; + +import android.os.Bundle; +import android.os.IBinder; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.util.SparseArray; + +import java.io.Serializable; +import java.util.ArrayList; + +/** + * Created by Kosh on 23 May 2016, 3:37 PM + */ + +public class Bundler { + + private Bundle bundle; + + private Bundler() { + bundle = new Bundle(); + } + + public static Bundler start() { + return new Bundler(); + } + + public Bundler put(@NonNull String key, boolean value) { + bundle.putBoolean(key, value); + return this; + } + + public Bundler put(@NonNull String key, boolean[] value) { + bundle.putBooleanArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, IBinder value) { + // Uncommment this line if your minimum sdk version is API level 18 + //start.putBinder(key, value); + return this; + } + + public Bundler put(@NonNull String key, int value) { + bundle.putInt(key, value); + return this; + } + + public Bundler put(@NonNull String key, int[] value) { + bundle.putIntArray(key, value); + return this; + } + + public Bundler putIntegerArrayList(@NonNull String key, ArrayList value) { + bundle.putIntegerArrayList(key, value); + return this; + } + + public Bundler put(@NonNull String key, Bundle value) { + bundle.putBundle(key, value); + return this; + } + + public Bundler put(@NonNull String key, byte value) { + bundle.putByte(key, value); + return this; + } + + public Bundler put(@NonNull String key, byte[] value) { + bundle.putByteArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, String value) { + bundle.putString(key, value); + return this; + } + + public Bundler put(@NonNull String key, String[] value) { + bundle.putStringArray(key, value); + return this; + } + + public Bundler putStringArrayList(@NonNull String key, ArrayList value) { + bundle.putStringArrayList(key, value); + return this; + } + + public Bundler put(@NonNull String key, long value) { + bundle.putLong(key, value); + return this; + } + + public Bundler put(@NonNull String key, long[] value) { + bundle.putLongArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, float value) { + bundle.putFloat(key, value); + return this; + } + + public Bundler put(@NonNull String key, float[] value) { + bundle.putFloatArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, char value) { + bundle.putChar(key, value); + return this; + } + + public Bundler put(@NonNull String key, char[] value) { + bundle.putCharArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, CharSequence value) { + bundle.putCharSequence(key, value); + return this; + } + + public Bundler put(@NonNull String key, CharSequence[] value) { + bundle.putCharSequenceArray(key, value); + return this; + } + + public Bundler putCharSequenceArrayList(@NonNull String key, ArrayList value) { + bundle.putCharSequenceArrayList(key, value); + return this; + } + + public Bundler put(@NonNull String key, double value) { + bundle.putDouble(key, value); + return this; + } + + public Bundler put(@NonNull String key, double[] value) { + bundle.putDoubleArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, Parcelable value) { + bundle.putParcelable(key, value); + return this; + } + + public Bundler put(@NonNull String key, Parcelable[] value) { + bundle.putParcelableArray(key, value); + return this; + } + + public Bundler putParcelableArrayList(@NonNull String key, ArrayList value) { + bundle.putParcelableArrayList(key, value); + return this; + } + + public Bundler putSparseParcelableArray(@NonNull String key, SparseArray value) { + bundle.putSparseParcelableArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, short value) { + bundle.putShort(key, value); + return this; + } + + public Bundler put(@NonNull String key, short[] value) { + bundle.putShortArray(key, value); + return this; + } + + public Bundler put(@NonNull String key, Serializable value) { + bundle.putSerializable(key, value); + return this; + } + + public Bundler putAll(Bundle map) { + bundle.putAll(map); + return this; + } + + /** + * Get the underlying start. + */ + public Bundle get() { + return bundle; + } + + public Bundle end() { + return get(); + } + +} diff --git a/app/src/main/java/com/fastaccess/helper/ColorHelper.java b/app/src/main/java/com/fastaccess/helper/ColorHelper.java new file mode 100644 index 0000000..bb331a9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/ColorHelper.java @@ -0,0 +1,52 @@ +package com.fastaccess.helper; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class ColorHelper { + + public static ColorHelper MATERIAL; + + static { + MATERIAL = create(Arrays.asList( + 0xffe57373, + 0xfff06292, + 0xffba68c8, + 0xff9575cd, + 0xff7986cb, + 0xff64b5f6, + 0xff4fc3f7, + 0xff4dd0e1, + 0xff4db6ac, + 0xff81c784, + 0xffaed581, + 0xffff8a65, + 0xffd4e157, + 0xffffd54f, + 0xffffb74d, + 0xffa1887f, + 0xff90a4ae + )); + } + + private final List mColors; + private final Random mRandom; + + public static ColorHelper create(List colorList) { + return new ColorHelper(colorList); + } + + private ColorHelper(List colorList) { + mColors = colorList; + mRandom = new Random(System.currentTimeMillis()); + } + + public int getRandomColor() { + return mColors.get(mRandom.nextInt(mColors.size())); + } + + public int getColor(Object key) { + return mColors.get(Math.abs(key.hashCode()) % mColors.size()); + } +} diff --git a/app/src/main/java/com/fastaccess/helper/DatabaseHelper.java b/app/src/main/java/com/fastaccess/helper/DatabaseHelper.java new file mode 100644 index 0000000..9d7f716 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/DatabaseHelper.java @@ -0,0 +1,11 @@ +package com.fastaccess.helper; + +/** + * Created by Kosh on 09 Aug 2016, 10:25 PM + */ + +public class DatabaseHelper { + + private DatabaseHelper() {} + +} diff --git a/app/src/main/java/com/fastaccess/helper/DateHelper.java b/app/src/main/java/com/fastaccess/helper/DateHelper.java new file mode 100644 index 0000000..813b8e0 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/DateHelper.java @@ -0,0 +1,293 @@ +package com.fastaccess.helper; + +import android.text.format.DateUtils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +/** + * Created by kosh20111 on 10/7/2015. CopyRights @ Innov8tif + *

+ * Helper Class to deal with time and dates + */ +public class DateHelper { + + public enum DateFormats { + D_YYMMDD("yy-MM-dd"), D_DDMMyy("dd-MM-yy"), + D_YYMMDD_N("yy-MMM-dd"), D_DDMMyy_N("dd-MMM-yy"), + D_YYMMDDHHMMA_N("yy-MMM-dd, hh:mma"), D_DDMMyyHHMMA_N("dd-MMM-yy, hh:mma"), + S_YYMMDD("yy/MM/dd"), S_DDMMyy("dd/MM/yy"), + S_YYMMDDHHMMA("yy/MM/dd, hh:mma"), S_DDMMyyHHMMA("dd/MM/yy, hh:mma"), + S_YYMMDDHHMMA_N("yy/MMM/dd, hh:mma"), S_DDMMyyHHMMA_N("dd/MMM/yy, hh:mma"), + D_YYYYMMDD("yyyy-MM-dd"), D_DDMMYYYY("dd-MM-yyyy"), + D_YYYYMMDDHHMMA("yyyy-MM-dd, hh:mma"), D_DDMMYYYYHHMMA("dd-MM-yyyy, hh:mma"), + D_YYYYMMDD_N("yyyy-MMM-dd"), D_DDMMYYYY_N("dd-MMM-yyyy"), + D_YYYYMMDDHHMMA_N("yyyy-MMM-dd, hh:mma"), D_DDMMYYYYHHMMA_N("dd-MMM-yyyy, hh:mma"), + S_YYYYMMDD("yyyy/MM/dd"), S_DDMMYYYY("dd/MM/yyyy"), + S_YYYYMMDDHHMMA("yyyy/MM/dd, hh:mma"), S_DDMMYYYYHHMMA("dd/MM/yyyy, hh:mma"), + S_YYYYMMDDHHMMA_N("yyyy/MMM/dd, hh:mma"), S_DDMMYYYYHHMMA_N("dd/MMM/yyyy, hh:mma"), + D_YYMMDDHHMMSSA_N("yy-MMM-dd, hh:mm:ssa"), D_DDMMyyHHMMSSA_N("dd-MMM-yy, hh:mm:ssa"), + S_YYMMDDHHMMSSA("yy/MM/dd, hh:mm:ssa"), S_DDMMyyHHMMSSA("dd/MM/yy, hh:mm:ssa"), + S_YYMMDDHHMMSSA_N("yy/MMM/dd, hh:mm:ssa"), S_DDMMyyHHMMSSA_N("dd/MMM/yy, hh:mm:ssa"), + D_YYYYMMDDHHMMSSA("yyyy-MM-dd, hh:mm:ssa"), D_DDMMYYYYHHMMSSA("dd-MM-yyyy, hh:mm:ssa"), + D_YYYYMMDDHHMMSSA_N("yyyy-MMM-dd, hh:mm:ssa"), D_DDMMYYYYHHMMSSA_N("dd-MMM-yyyy, hh:mm:ssa"), + S_YYYYMMDDHHMMSSA("yyyy/MM/dd, hh:mm:ssa"), S_DDMMYYYYHHMMSSA("dd/MM/yyyy, hh:mm:ssa"), + S_YYYYMMDDHHMMSSA_N("yyyy/MMM/dd, hh:mm:ssa"), S_DDMMYYYYHHMMSSA_N("dd/MMM/yyyy, hh:mm:ssa"), + YYMMDDHHMMSS("yyMMddhhmmss"), YYMMDDHHMMSS_24("yyMMddkkmmss"), + HHMMA("hh:mma"), HHMM("hh:mm"), HHMMSSA("hh:mm:ssa"), HHMMSS("hh:mm:ss"), + DD("dd"), MM("MM"), MM_N("MMM"), DDMM("dd MM"), DDMM_N("dd MMM"), DDMMYYYY("ddMMyyyy"); + private String dateFormat; + + DateFormats(String dateFormat) {this.dateFormat = dateFormat;} + + public String getDateFormat() { + return dateFormat; + } + } + + /** + * @return hh:mm a || dd MMM hh:mm a + */ + public static String prettifyDate(long timestamp) { + SimpleDateFormat dateFormat; + if (DateUtils.isToday(timestamp)) { + dateFormat = new SimpleDateFormat("hh:mm a", Locale.getDefault()); + } else { + dateFormat = new SimpleDateFormat("dd MMM hh:mm a", Locale.getDefault()); + } + return dateFormat.format(timestamp); + } + + /** + * @return hh:mm a || dd MMM hh:mm a + */ + public static String prettifyDate(String timestamp, DateFormats dateFormats) { + SimpleDateFormat sample = new SimpleDateFormat(dateFormats.getDateFormat(), Locale.getDefault()); + + try { + long time = sample.parse(timestamp).getTime(); + if (DateUtils.isToday(time)) { + sample = new SimpleDateFormat("hh:mm a", Locale.getDefault()); + } else { + sample = new SimpleDateFormat("dd MMM hh:mm a", Locale.getDefault()); + } + return sample.format(time); + } catch (Exception e) { + e.printStackTrace(); + } + return timestamp; + } + + /** + * @return dd/MM/yyyy + */ + public static long getDateOnly(String date) { + SimpleDateFormat sample = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()); + try { + return sample.parse(date).getTime(); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + /** + * @return dd/MM/yyyy + */ + public static String getDateOnly(long time) { + return new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(time); + } + + /** + * @return dd/MM/yyyy, hh:mm a + */ + public static String getDateAndTime(long time) { + SimpleDateFormat sample = new SimpleDateFormat("dd/MM/yyyy, hh:mm a", Locale.getDefault()); + return sample.format(new Date(time)); + } + + /** + * @return dd/MM/yyyy, hh:mm a + */ + public static String getDateAndTime(String time) { + SimpleDateFormat sample = new SimpleDateFormat("dd/MM/yyyy, hh:mm a", Locale.getDefault()); + return sample.format(time); + } + + /** + * @return hh:mm a + */ + public static String getTimeOnly(long time) { + SimpleDateFormat sample = new SimpleDateFormat("hh:mm a", Locale.getDefault()); + return sample.format(time); + } + + /** + * @return today's date in format (dd/MM/yyyy HH:mm:ss) + */ + public static String getTodayWithTime() { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss", Locale.getDefault()); + return dateFormat.format(new Date()); + } + + /** + * @return today's date in format (dd/MM/yyyy) + */ + public static String getToday() { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()); + return dateFormat.format(new Date()); + } + + public static String getYesterday() { + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).parse(getToday())); + calendar.add(Calendar.DATE, -1); + Date tomorrow = calendar.getTime(); + return new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(tomorrow); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * @return tomorrows's date in format (dd/MM/yyyy) + */ + public static String getTomorrow() { + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).parse(getToday())); + calendar.add(Calendar.DATE, 1); + Date tomorrow = calendar.getTime(); + return new SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()).format(tomorrow); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * new and old date must equal to {@link DateFormats} + * + * @return number of hours + */ + public static long getDaysBetweenTwoDate(String old, String newDate, DateFormats dateFormats) { + SimpleDateFormat myFormat = new SimpleDateFormat(dateFormats.getDateFormat(), Locale.getDefault()); + try { + Date date1 = myFormat.parse(old); + Date date2 = myFormat.parse(newDate); + long diff = date1.getTime() - date2.getTime(); + long seconds = diff / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + return hours / 24; + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + /** + * new and old date must equal to {@link DateFormats} + * + * @return number of hours + */ + public static long getHoursBetweenTwoDate(String old, String newDate, DateFormats dateFormats) { + SimpleDateFormat myFormat = new SimpleDateFormat(dateFormats.getDateFormat(), Locale.getDefault()); + try { + Date date1 = myFormat.parse(old); + Date date2 = myFormat.parse(newDate); + long diff = date1.getTime() - date2.getTime(); + long seconds = diff / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + long days = hours / 24; + return hours; + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + public static long getMinutesBetweenTwoDates(String old, String newDate, DateFormats dateFormats) { + SimpleDateFormat myFormat = new SimpleDateFormat(dateFormats.getDateFormat(), Locale.getDefault()); + try { + Date date1 = myFormat.parse(old); + Date date2 = myFormat.parse(newDate); + long diff = date1.getTime() - date2.getTime(); + long seconds = diff / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + long days = hours / 24; + return minutes; + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + public static long getMinutesBetweenTwoDates(long old, long newDate) { + long diff = old - newDate; + long seconds = diff / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + long days = hours / 24; + return minutes; + } + + public static boolean isInFuture(long timestamp) { + Date date = new Date(timestamp); + return date.getTime() - new Date().getTime() >= 0; + } + + /** + */ + public static long parseAnyDate(String date) { + long time = 0; + for (DateFormats formats : DateFormats.values()) { + try { + SimpleDateFormat format = new SimpleDateFormat(formats.getDateFormat(), Locale.getDefault()); + time = format.parse(date).getTime(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return time; + } + + public static long parseDate(String date, DateFormats dateFormats) { + SimpleDateFormat format = new SimpleDateFormat(dateFormats.getDateFormat(), Locale.getDefault()); + try { + return format.parse(date).getTime(); + } catch (Exception e) { + e.printStackTrace(); + } + return 0; + } + + public static String getDate(String date, DateFormats orginalFormat, DateFormats newFormat) { + SimpleDateFormat sample = new SimpleDateFormat(orginalFormat.getDateFormat(), Locale.getDefault()); + try { + long time = sample.parse(date).getTime(); + sample = new SimpleDateFormat(newFormat.getDateFormat(), Locale.getDefault()); + return sample.format(time); + } catch (Exception e) { + e.printStackTrace(); + } + return date; + } + + public static String getDesiredFormat(DateFormats formats) { + SimpleDateFormat format = new SimpleDateFormat(formats.getDateFormat(), Locale.getDefault()); + return format.format(new Date()); + } + + public static String getDesiredFormat(DateFormats formats, long date) { + if (date == 0) return ""; + SimpleDateFormat format = new SimpleDateFormat(formats.getDateFormat(), Locale.getDefault()); + return format.format(date); + } +} diff --git a/app/src/main/java/com/fastaccess/helper/FileHelper.java b/app/src/main/java/com/fastaccess/helper/FileHelper.java new file mode 100644 index 0000000..dba4363 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/FileHelper.java @@ -0,0 +1,162 @@ +package com.fastaccess.helper; + +import android.content.Context; +import android.os.Environment; +import android.text.TextUtils; +import android.webkit.MimeTypeMap; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kosh20111 on 10/7/2015. CopyRights @ Innov8tif + */ +public class FileHelper { + + private static String folderName; + private String TAG = this.getClass().getSimpleName(); + + public static void initFolderName(String fName) { + folderName = Environment.getExternalStorageDirectory() + "/" + fName; + } + + public static File folderName() { + File file = new File(folderName); + if (!file.exists()) + file.mkdir(); + return file; + } + + public static String getBaseFolderName() { + return folderName; + } + + public static File getFile(String path) { + return new File(path); + } + + private static String getPng(String path) { + return path + ".png"; + } + + public static boolean deleteFile(String path) { + if (!TextUtils.isEmpty(path)) { + File file = new File(path); + if (file.exists()) { + return file.delete(); + } else { + file = new File(folderName(), path); + if (file.exists()) { + return file.delete(); + } + } + } + return false; + } + + public static void deleteFile(List paths) { + for (String path : paths) { + if (path != null) { + File file = new File(folderName(), path); + if (file.exists()) { + file.delete(); + } + } + } + } + + public static String generateFileName(String packageName) { + return getPng(packageName); + } + + public static File generateFile(String path) { + File file = new File(folderName, ".nomedia"); + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + file.mkdir(); + } + } + return new File(folderName(), generateFileName(path)); + } + + private static void generateDefaultFile() { + File file = new File(folderName); + if (!file.exists()) { + file.mkdir(); + } + } + + public static File generateZipFile(String name) { + File file = new File(folderName, ".nomedia"); + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + file.mkdir(); + } + } + return new File(folderName(), name + ".zip"); + } + + public static File generateFolder(String name) { + File file = new File(folderName); + if (!file.exists()) { + file.mkdir(); + } + File folderName = new File(file, name); + if (!folderName.exists()) { + folderName.mkdirs(); + } + return folderName; + } + + public static String getCacheFile(Context context, String packageName) { + return context.getCacheDir().getPath() + "/" + generateFileName(packageName); + } + + public static boolean exists(String path) { + return getFile(path).exists(); + } + + public static List getFiles(File dir) { + List files = new ArrayList<>(); + File listFile[] = dir.listFiles(); + if (listFile != null && listFile.length > 0) { + for (File aListFile : listFile) { + if (aListFile.isDirectory()) { + getFiles(aListFile); + } else { + if (aListFile.getName().endsWith(".png") || aListFile.getName().endsWith(".jpg") + || aListFile.getName().endsWith(".jpeg") || aListFile.getName().endsWith(".gif")) { + files.add(aListFile); + } + } + + } + } + return files; + } + + public static String getInternalDirectoryPath() { + return Environment.getExternalStorageDirectory().getAbsolutePath(); + } + + public static String getSDcardDirectoryPath() { + return System.getenv("SECONDARY_STORAGE"); + } + + public static String getMimeType(String file) { + return MimeTypeMap.getFileExtensionFromUrl(file); + } + + public static String extension(String file) { + return MimeTypeMap.getFileExtensionFromUrl(file); + } + +} diff --git a/app/src/main/java/com/fastaccess/helper/GsonHelper.java b/app/src/main/java/com/fastaccess/helper/GsonHelper.java new file mode 100644 index 0000000..5644fb5 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/GsonHelper.java @@ -0,0 +1,31 @@ +package com.fastaccess.helper; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; + +/** + * Created by Kosh on 29 May 2016, 5:09 AM + */ + +public class GsonHelper { + + public static T getObject(String json, Class clazz) { + return gson().fromJson(json, clazz); + } + + public static List getList(String json, Class clazz) { + return Arrays.asList(gson().fromJson(json, clazz)); + } + + public static Gson gson() { + return new GsonBuilder() + .excludeFieldsWithModifiers(Modifier.FINAL, Modifier.TRANSIENT, Modifier.STATIC) + .setPrettyPrinting() + .disableHtmlEscaping() + .create(); + } +} diff --git a/app/src/main/java/com/fastaccess/helper/InputHelper.java b/app/src/main/java/com/fastaccess/helper/InputHelper.java new file mode 100644 index 0000000..3b414fd --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/InputHelper.java @@ -0,0 +1,87 @@ +package com.fastaccess.helper; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.design.widget.TextInputLayout; +import android.text.TextUtils; +import android.text.format.Formatter; +import android.widget.EditText; +import android.widget.TextView; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; + +/** + * Created by kosh20111 on 3/11/2015. CopyRights @ Innov8tif + *

+ * Input Helper to validate stuff related to input fields. + */ +public class InputHelper { + + + private static boolean isWhiteSpaces(String s) { + return s != null && s.matches("\\s+"); + } + + public static boolean isEmpty(String text) { + return text == null || TextUtils.isEmpty(text) || isWhiteSpaces(text); + } + + public static boolean isEmpty(Object text) { + return text == null || TextUtils.isEmpty(text.toString()) || isWhiteSpaces(text.toString()); + } + + public static boolean isEmpty(EditText text) { + return text == null || isEmpty(text.getText().toString()); + } + + public static boolean isEmpty(TextView text) { + return text == null || isEmpty(text.getText().toString()); + } + + public static boolean isEmpty(TextInputLayout txt) { + return txt == null || isEmpty(txt.getEditText()); + } + + public static String toNA(String value) { + return isEmpty(value) ? "N/A" : value; + } + + public static String toString(EditText editText) { + return editText.getText().toString(); + } + + public static String toString(TextView editText) { + return editText.getText().toString(); + } + + public static String toString(TextInputLayout textInputLayout) { + return toString(textInputLayout.getEditText()); + } + + @NonNull public static String toString(@NonNull Object object) { + return !isEmpty(object) ? object.toString() : ""; + } + + public static String formatSize(Context context, long size) { + return Formatter.formatShortFileSize(context, size); + } + + public static String formatPrice(double doubleValue) { + NumberFormat nf = NumberFormat.getCurrencyInstance(); + DecimalFormatSymbols decimalFormatSymbols = ((DecimalFormat) nf).getDecimalFormatSymbols(); + decimalFormatSymbols.setCurrencySymbol(""); + ((DecimalFormat) nf).setDecimalFormatSymbols(decimalFormatSymbols); + return nf.format(doubleValue).trim(); + } + + public static String ordinal(int i) { + return i % 100 == 11 || i % 100 == 12 || i % 100 == 13 ? i + "th" : i + + new String[]{"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"}[i % 10]; + } + + @NonNull public static String getTwoLetters(@NonNull String value) { + return value.length() > 1 ? (String.valueOf(value.charAt(0)) + String.valueOf(value.charAt(1))) : String.valueOf(value.charAt(0)); + } +} diff --git a/app/src/main/java/com/fastaccess/helper/Logger.java b/app/src/main/java/com/fastaccess/helper/Logger.java new file mode 100644 index 0000000..b53a7b1 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/Logger.java @@ -0,0 +1,96 @@ +package com.fastaccess.helper; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.util.Arrays; +import java.util.List; + +import static com.fastaccess.helper.GsonHelper.gson; + +/** + * Created by Kosh on 04/12/15 11:52 PM. copyrights @ Innov8tif + */ +public class Logger { + + private final static String TAG = "Logger"; + + private static void e(@NonNull String tag, @Nullable Object text) { + Log.e(tag, text != null ? text.toString() : "LOGGER IS NULL");//avoid null + } + + private static void d(@NonNull String tag, @Nullable Object text) { + Log.d(tag, text != null ? text.toString() : "LOGGER IS NULL");//avoid null + } + + private static void i(@NonNull String tag, @Nullable Object text) { + Log.i(tag, text != null ? text.toString() : "LOGGER IS NULL");//avoid null + } + + public static void e(@Nullable Object text) {e(getCurrentClassName() + " || " + getCurrentMethodName(), text);} + + public static void d(@Nullable Object text) { + d(getCurrentClassName() + " || " + getCurrentMethodName(), text);//avoid null + } + + public static void i(@Nullable Object text) { + i(getCurrentClassName() + " || " + getCurrentMethodName(), text);//avoid null + } + + public static void e(Object... objects) { + if (objects != null && objects.length > 0) { + e(getCurrentClassName() + " || " + getCurrentMethodName(), Arrays.toString(objects)); + } else { + e(getCurrentClassName() + " || " + getCurrentMethodName(), getCurrentMethodName()); + } + } + + public static void e(List objects) { + if (objects != null) { + e(getCurrentClassName() + " || " + getCurrentMethodName(), Arrays.toString(objects.toArray())); + } else { + e(TAG, null); + } + } + + public static void longE(@NonNull Object text) { + String veryLongString = text.toString(); + int maxLogSize = 4000; + for (int i = 0; i <= veryLongString.length() / maxLogSize; i++) { + int start = i * maxLogSize; + int end = (i + 1) * maxLogSize; + end = end > veryLongString.length() ? veryLongString.length() : end; + e(getCurrentClassName() + " || " + getCurrentMethodName(), veryLongString.substring(start, end)); + } + + } + + public static void eJson(Object object) { + e(getCurrentClassName() + " || " + getCurrentMethodName(), gson().toJson(object)); + } + + private static String getCurrentMethodName() { + try { + return Thread.currentThread().getStackTrace()[4].getMethodName() + "()"; + } catch (Exception ignored) {} + return TAG; + } + + private static String getCurrentLineNumber() { + try { + return " :" + Thread.currentThread().getStackTrace()[4].getLineNumber(); + } catch (Exception ignored) {} + return TAG; + } + + private static String getCurrentClassName() { + try { + String className = Thread.currentThread().getStackTrace()[4].getClassName(); + String[] temp = className.split("[\\.]"); + className = temp[temp.length - 1]; + return className; + } catch (Exception ignored) {} + return TAG; + } +} diff --git a/app/src/main/java/com/fastaccess/helper/NotificationHelper.java b/app/src/main/java/com/fastaccess/helper/NotificationHelper.java new file mode 100755 index 0000000..7be6b62 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/NotificationHelper.java @@ -0,0 +1,145 @@ +package com.fastaccess.helper; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; + +import com.fastaccess.R; +import com.fastaccess.provider.service.FloatingService; + +/** + * Created by kosh20111 on 9/8/2015. CopyRights @ Innov8tif + */ +public class NotificationHelper { + + public static final int NOTIFICATION_ID = 20111; + + public static void notifyShort(@NonNull Context context, @NonNull String title, @NonNull String msg, @DrawableRes int iconId) { + notifyShort(context, title, msg, iconId, NOTIFICATION_ID, null); + } + + public static void notifyShort(@NonNull Context context, @NonNull String title, @NonNull String msg, @DrawableRes int iconId, + @NonNull PendingIntent pendingIntent) { + notifyShort(context, title, msg, iconId, NOTIFICATION_ID, pendingIntent); + } + + public static void notifyShort(@NonNull Context context, @NonNull String title, @NonNull String msg, @DrawableRes int iconId, int nId) { + notifyShort(context, title, msg, iconId, nId, null); + } + + public static void notifyShort(@NonNull Context context, @NonNull String title, @NonNull String msg, @DrawableRes int iconId, int nId, + @Nullable PendingIntent pendingIntent) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new NotificationCompat.Builder(context) + .setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setContentTitle(title) + .setContentText(msg) + .setSmallIcon(iconId) + .setContentIntent(pendingIntent) + .build(); + notificationManager.notify(nId, notification); + } + + public static void notifyShort(@NonNull Context context, @NonNull String title, String msg, @DrawableRes int iconId, + @NonNull NotificationCompat.Action action) { + notifyShort(context, title, msg, iconId, action, NOTIFICATION_ID); + } + + public static void notifyShort(@NonNull Context context, @NonNull String title, String msg, @DrawableRes int iconId, + @NonNull NotificationCompat.Action action, int nId) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new NotificationCompat.Builder(context) + .setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setContentTitle(title) + .setContentText(msg) + .setSmallIcon(iconId) + .addAction(action) + .setContentIntent(action.actionIntent) + .build(); + notificationManager.notify(nId, notification); + } + + public static void notifyBig(@NonNull Context context, @NonNull String title, @NonNull String msg, @DrawableRes int iconId) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new NotificationCompat.Builder(context) + .setAutoCancel(true) + .setContentTitle(title) + .setDefaults(Notification.DEFAULT_ALL) + .setContentText(msg) + .setSmallIcon(iconId) + .setStyle(new NotificationCompat.BigTextStyle().setBigContentTitle(title).setSummaryText(msg).bigText(msg)) + .build(); + notificationManager.notify(NOTIFICATION_ID, notification); + } + + public static void notifyWithImage(@NonNull Context context, @NonNull String title, @NonNull String msg, @DrawableRes int iconId, + @NonNull Bitmap bitmap) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + Notification notification = new NotificationCompat.Builder(context) + .setAutoCancel(true) + .setDefaults(Notification.DEFAULT_ALL) + .setContentTitle(title) + .setContentText(msg) + .setSmallIcon(iconId) + .setStyle(new NotificationCompat.BigPictureStyle().setBigContentTitle(title).setSummaryText(msg).bigPicture(bitmap)) + .build(); + notificationManager.notify(NOTIFICATION_ID, notification); + } + + public static Notification getNonCancellableNotification(@NonNull Context content, @NonNull String title, @NonNull String msg, + @DrawableRes int iconId, @NonNull PendingIntent pendingIntent) { + return new NotificationCompat.Builder(content) + .setAutoCancel(false) + .setOngoing(true) + .setContentTitle(title) + .setContentText(msg) + .setSmallIcon(iconId) + .setContentIntent(pendingIntent) + .build(); + } + + public static void collapseFAService(Context context, int size) { + context.stopService(new Intent(context, FloatingService.class)); + Intent notificationIntent = new Intent(context, FloatingService.class); + notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent pendingIntent = PendingIntent.getService(context, 0, notificationIntent, 0); + NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context); + int icon = R.drawable.ic_fa_notification; + long finalTime = System.currentTimeMillis(); + if (PrefHelper.getBoolean(PrefConstant.STATUS_BAR_HIDDEN)) { + icon = R.drawable.ic_notification; + } + notificationBuilder + .setPriority(Notification.PRIORITY_LOW) + .setWhen(finalTime) + .setSmallIcon(icon) + .setContentTitle(context.getString(R.string.app_name)) + .setContentText(context.getString(R.string.click_to_start_service)) + .setNumber(size) + .setAutoCancel(false) + .setOngoing(true); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationBuilder.setContentIntent(pendingIntent); + notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build()); + } + + public static void cancelNotification(@NonNull Context context, int id) { + int finalId = id == 0 ? NOTIFICATION_ID : id; + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(finalId); + } + + public static void cancelAllNotifications(@NonNull Context context) { + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); + } +} diff --git a/app/src/main/java/com/fastaccess/helper/PaletteHelper.java b/app/src/main/java/com/fastaccess/helper/PaletteHelper.java new file mode 100644 index 0000000..f08a0db --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/PaletteHelper.java @@ -0,0 +1,69 @@ +package com.fastaccess.helper; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.graphics.Palette; + +/** + * Created by Kosh on 11 May 2016, 8:42 PM + */ +public class PaletteHelper { + + public static final int PRIMARY_COLOR = Color.parseColor("#FF2A456B"); + + public interface OnColorExtraction { + void onColorExtracted(int color, int textColor); + } + + public static void extractColor(@Nullable Bitmap bitmap, @NonNull final OnColorExtraction onColorExtraction) { + if (bitmap == null) { + onColorExtraction.onColorExtracted(PRIMARY_COLOR, Color.WHITE); + return; + } + Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() { + @Override public void onGenerated(Palette palette) { + if (palette == null) { + onColorExtraction.onColorExtracted(PRIMARY_COLOR, Color.WHITE); + return; + } + int color = PRIMARY_COLOR; + int textColor = Color.WHITE; + if (palette.getVibrantSwatch() != null) { + Palette.Swatch muted = palette.getVibrantSwatch(); + textColor = muted.getTitleTextColor(); + color = muted.getRgb(); + } else if (palette.getLightVibrantSwatch() != null) { + Palette.Swatch muted = palette.getLightVibrantSwatch(); + color = muted.getRgb(); + textColor = muted.getTitleTextColor(); + } else if (palette.getDarkVibrantSwatch() != null) { + Palette.Swatch muted = palette.getDarkVibrantSwatch(); + color = muted.getRgb(); + textColor = muted.getTitleTextColor(); + } else if (palette.getLightMutedSwatch() != null) { + Palette.Swatch muted = palette.getLightMutedSwatch(); + color = muted.getRgb(); + textColor = muted.getTitleTextColor(); + } else if (palette.getMutedSwatch() != null) { + Palette.Swatch muted = palette.getMutedSwatch(); + color = muted.getRgb(); + textColor = muted.getTitleTextColor(); + } + onColorExtraction.onColorExtracted(color, textColor); + } + }); + } + + public static void extractColor(@Nullable Drawable drawable, @NonNull OnColorExtraction onColorExtraction) { + if (drawable == null) { + onColorExtraction.onColorExtracted(PRIMARY_COLOR, Color.WHITE); + return; + } + extractColor(((BitmapDrawable) drawable).getBitmap(), onColorExtraction); + } + +} diff --git a/app/src/main/java/com/fastaccess/helper/ParcelableBooleanParse.java b/app/src/main/java/com/fastaccess/helper/ParcelableBooleanParse.java new file mode 100644 index 0000000..67c0b00 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/ParcelableBooleanParse.java @@ -0,0 +1,58 @@ +package com.fastaccess.helper; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.SparseBooleanArray; + +/** + * Created by Kosh on 13/12/15 3:04 PM + */ +public class ParcelableBooleanParse extends SparseBooleanArray implements Parcelable { + + public static Creator CREATOR = new Creator() { + @Override public ParcelableBooleanParse createFromParcel(Parcel source) { + ParcelableBooleanParse read = new ParcelableBooleanParse(); + int size = source.readInt(); + int[] keys = new int[size]; + boolean[] values = new boolean[size]; + source.readIntArray(keys); + source.readBooleanArray(values); + for (int i = 0; i < size; i++) { + read.put(keys[i], values[i]); + } + return read; + } + + @Override public ParcelableBooleanParse[] newArray(int size) { + return new ParcelableBooleanParse[size]; + } + }; + + public ParcelableBooleanParse() { + + } + + public ParcelableBooleanParse(SparseBooleanArray sparseBooleanArray) { + for (int i = 0; i < sparseBooleanArray.size(); i++) { + this.put(sparseBooleanArray.keyAt(i), sparseBooleanArray.valueAt(i)); + } + } + + @Override public int describeContents() { + return 0; + } + + @Override public void writeToParcel(Parcel dest, int flags) { + int[] keys = new int[size()]; + boolean[] values = new boolean[size()]; + + for (int i = 0; i < size(); i++) { + keys[i] = keyAt(i); + values[i] = valueAt(i); + } + + dest.writeInt(size()); + dest.writeIntArray(keys); + dest.writeBooleanArray(values); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/helper/PermissionsHelper.java b/app/src/main/java/com/fastaccess/helper/PermissionsHelper.java new file mode 100644 index 0000000..6e6d08d --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/PermissionsHelper.java @@ -0,0 +1,34 @@ +package com.fastaccess.helper; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.provider.Settings; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; + +/** + * Created by Kosh on 19 Oct 2016, 6:55 PM + */ + +public class PermissionsHelper { + + public static final int OVERLAY_PERMISSION_REQ_CODE = 1; + + @TargetApi(Build.VERSION_CODES.M) public static boolean isSystemAlertGranted(@NonNull Context context) { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays(context); + } + + @TargetApi(Build.VERSION_CODES.M) public static boolean systemAlertPermissionIsGranted(@NonNull AppCompatActivity context) { + if (!isSystemAlertGranted(context)) { + Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + context.getPackageName())); + context.startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); + return false; + } + return true; + } +} + + diff --git a/app/src/main/java/com/fastaccess/helper/PrefConstant.java b/app/src/main/java/com/fastaccess/helper/PrefConstant.java new file mode 100644 index 0000000..bb8aa7f --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/PrefConstant.java @@ -0,0 +1,88 @@ +package com.fastaccess.helper; + +import android.content.Context; +import android.content.res.Resources; +import android.support.annotation.NonNull; +import android.view.ViewGroup; + +import com.fastaccess.R; + +/** + * Created by Kosh on 16 Oct 2016, 4:48 AM + */ + +public class PrefConstant { + + public static final String FLOATING_MODE = "floating_mode"; + public static final String FA_AUTO_SAVE_POSITION = "fa_auto_save_position"; + public static final String FA_EDGES_STICKY = "fa_edges_sticky"; + public static final String FA_ALWAYS_SHOWING = "fa_always_showing"; + public static final String STATUS_BAR_HIDDEN = "status_bar_hidden"; + public static final String FA_BACKGROUND = "fa_background"; + public static final String FA_BACKGROUND_ALPHA = "fa_background_alpha"; + public static final String ICON_PADDING = "icon_padding"; + public static final String ICON_PACK = "icon_pack"; + public static final String CUSTOM_ICON = "custom_icon"; + public static final String AUTO_TRANS = "auto_trans"; + public static final String ICON_ALPHA = "icon_alpha"; + public static final String ICON_SIZE = "icon_size"; + public static final String MANUAL_SIZE = "manual_size"; + public static final String POSITION_X = "floating_position_x"; + public static final String POSITION_Y = "floating_position_y"; + public static final String FA_AUTO_START = "fa_auto_start"; + public static final String FA_IS_HORIZONTAL = "fa_is_horizontal"; + + public static void savePosition(int x, int y) { + boolean isAutoSavePosition = PrefHelper.getBoolean(PrefConstant.FA_AUTO_SAVE_POSITION); + if (isAutoSavePosition) { + Logger.e(x, y); + PrefHelper.set(POSITION_X, x); + PrefHelper.set(POSITION_Y, y); + } + } + + public static int getFinalSize(@NonNull Context context) { + int imageSize = ViewGroup.MarginLayoutParams.WRAP_CONTENT; + int iconSize = PrefHelper.getInt(PrefConstant.MANUAL_SIZE); + String size = PrefHelper.getString(PrefConstant.ICON_SIZE); + if (iconSize > 0) { + imageSize = ViewHelper.toPx(context, iconSize); + } else { + if (InputHelper.isEmpty(size)) size = "medium"; + if (size.equalsIgnoreCase("small")) { + imageSize = context.getResources().getDimensionPixelSize(R.dimen.fa_size_small); + } else if (size.equalsIgnoreCase("medium")) { + imageSize = context.getResources().getDimensionPixelSize(R.dimen.fa_size_medium); + } else if (size.equalsIgnoreCase("large")) { + imageSize = context.getResources().getDimensionPixelSize(R.dimen.fa_size_large); + } + } + return imageSize; + } + + public static int getGapSize(@NonNull Resources resources) { + String gap = PrefHelper.getString(ICON_PADDING); + int gapSize = resources.getDimensionPixelSize(R.dimen.spacing_normal); + if (!InputHelper.isEmpty(gap)) { + if (gap.equalsIgnoreCase("small")) { + gapSize = resources.getDimensionPixelSize(R.dimen.spacing_micro); + } else if (gap.equalsIgnoreCase("medium")) { + gapSize = resources.getDimensionPixelSize(R.dimen.spacing_normal); + + } else if (gap.equalsIgnoreCase("large")) { + gapSize = resources.getDimensionPixelSize(R.dimen.spacing_xs_large); + } else { + gapSize = resources.getDimensionPixelSize(R.dimen.spacing_normal); + } + } + return gapSize; + } + + public static boolean isAutoStart() { + return PrefHelper.getBoolean(FA_AUTO_START); + } + + public static boolean isHorizontal() { + return PrefHelper.getBoolean(FA_IS_HORIZONTAL); + } +} diff --git a/app/src/main/java/com/fastaccess/helper/PrefHelper.java b/app/src/main/java/com/fastaccess/helper/PrefHelper.java new file mode 100644 index 0000000..d8b4b80 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/PrefHelper.java @@ -0,0 +1,121 @@ +package com.fastaccess.helper; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.fastaccess.helper.GsonHelper.gson; + +/** + * Created by kosh20111 on 18 Oct 2016, 9:29 PM + */ +public class PrefHelper { + private static PrefHelper prefHelper; + private Context context; + + private PrefHelper(Context context) { + this.context = context; + } + + public static void init(@NonNull Context context) { + if (prefHelper == null) { + prefHelper = new PrefHelper(context.getApplicationContext()); + } + } + + /** + * @param key + * ( the Key to used to retrieve this data later ) + * @param value + * ( any kind of primitive values ) + *

+ * non can be null!!! + */ + public static void set(@NonNull String key, @NonNull Object value) { + if (InputHelper.isEmpty(key)) { + throw new NullPointerException("Key must not be null! (key = " + key + "), (value = " + value + ")"); + } + Logger.e(key, value); + SharedPreferences.Editor edit = PreferenceManager.getDefaultSharedPreferences(prefHelper.context).edit(); + if (value instanceof String) { + edit.putString(key, (String) value); + } else if (value instanceof Integer) { + edit.putInt(key, (int) value); + } else if (value instanceof Long) { + edit.putLong(key, (long) value); + } else if (value instanceof Boolean) { + edit.putBoolean(key, (boolean) value); + } else if (value instanceof Float) { + edit.putFloat(key, (float) value); + } else { + edit.putString(key, gson().toJson(value)); + } + edit.apply(); + } + + @Nullable public static T getJsonObject(@NonNull String key, @NonNull Class type) { + String value = getString(key); + if (!InputHelper.isEmpty(value)) { + return gson().fromJson(value, type); + } + return null; + } + + @Nullable public static List getJsonArray(@NonNull String key, final @NonNull Class type) { + String value = getString(key); + if (!InputHelper.isEmpty(value)) { + return Arrays.asList(gson().fromJson(value, type)); + } + return null; + } + + @Nullable public static String getString(@NonNull String key) { + return PreferenceManager.getDefaultSharedPreferences(prefHelper.context).getString(key, null); + } + + public static boolean getBoolean(@NonNull String key) { + return PreferenceManager.getDefaultSharedPreferences(prefHelper.context).getBoolean(key, false); + } + + public static int getInt(@NonNull String key) { + return PreferenceManager.getDefaultSharedPreferences(prefHelper.context).getInt(key, 0); + } + + public static long getLong(@NonNull String key) { + return PreferenceManager.getDefaultSharedPreferences(prefHelper.context).getLong(key, 0); + } + + public static float getFloat(@NonNull String key) { + return PreferenceManager.getDefaultSharedPreferences(prefHelper.context).getFloat(key, 0); + } + + public static void clearKey(@NonNull String key) { + PreferenceManager.getDefaultSharedPreferences(prefHelper.context).edit().remove(key).apply(); + } + + public static boolean isExist(@NonNull String key) { + return PreferenceManager.getDefaultSharedPreferences(prefHelper.context).contains(key); + } + + public static void clearPrefs() { + PreferenceManager.getDefaultSharedPreferences(prefHelper.context).edit().clear().apply(); + } + + public static Map getAll() { + Map toBackupMap = new HashMap<>(); + Map prefs = PreferenceManager.getDefaultSharedPreferences(prefHelper.context).getAll(); + for (String key : prefs.keySet()) { + if (!InputHelper.isEmpty(key) && !key.equalsIgnoreCase("null")) {// sometimes key is null, for no fucking reason. + toBackupMap.put(key, prefs.get(key)); + } + } + return toBackupMap; + } +} diff --git a/app/src/main/java/com/fastaccess/helper/TypeFaceHelper.java b/app/src/main/java/com/fastaccess/helper/TypeFaceHelper.java new file mode 100644 index 0000000..baf340f --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/TypeFaceHelper.java @@ -0,0 +1,25 @@ +package com.fastaccess.helper; + +import android.content.Context; +import android.graphics.Typeface; +import android.widget.TextView; + +/** + * Created by Kosh on 17/12/15 10:25 PM + */ +public class TypeFaceHelper { + + private static Typeface arabicTypeFace; + + public static void generateTypeface(Context context) { + arabicTypeFace = Typeface.createFromAsset(context.getAssets(), "fonts/app_font.ttf"); + } + + public static void applyTypeface(TextView textView) { + textView.setTypeface(arabicTypeFace); + } + + public static Typeface getTypeface() { + return arabicTypeFace; + } +} diff --git a/app/src/main/java/com/fastaccess/helper/ViewHelper.java b/app/src/main/java/com/fastaccess/helper/ViewHelper.java new file mode 100644 index 0000000..f8e4f98 --- /dev/null +++ b/app/src/main/java/com/fastaccess/helper/ViewHelper.java @@ -0,0 +1,243 @@ +package com.fastaccess.helper; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.RippleDrawable; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.StateListDrawable; +import android.graphics.drawable.shapes.RoundRectShape; +import android.support.annotation.ColorInt; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.design.widget.NavigationView; +import android.support.v4.content.ContextCompat; +import android.support.v4.graphics.drawable.DrawableCompat; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.fastaccess.R; +import com.tooltip.OnDismissListener; +import com.tooltip.Tooltip; + +import java.util.Arrays; + + +/** + * Created by kosh20111 on 10/7/2015 10:42 PM + */ +public class ViewHelper { + + public static int getAccentColor(Context context) { + TypedValue typedValue = new TypedValue(); + TypedArray a = context.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorAccent}); + int color = a.getColor(0, 0); + a.recycle(); + return color; + } + + public static int getPrimaryColor(Context context) { + TypedValue typedValue = new TypedValue(); + TypedArray a = context.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimary}); + int color = a.getColor(0, 0); + a.recycle(); + return color; + } + + public static int getPrimaryDarkColor(Context context) { + TypedValue typedValue = new TypedValue(); + TypedArray a = context.obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorPrimaryDark}); + int color = a.getColor(0, 0); + a.recycle(); + return color; + } + + public static int toPx(Context context, int dp) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, dp, context.getResources().getDisplayMetrics()); + } + + public static int toDp(Context context, int px) { + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, px, context.getResources().getDisplayMetrics()); + } + + public static Drawable tintDrawable(Drawable drawable, int color) { + drawable = DrawableCompat.wrap(drawable); + DrawableCompat.setTint(drawable, color); + return drawable; + } + + public static Drawable getDrawableSelector(int normalColor, int pressedColor) { + if (AppHelper.isLollipopOrHigher()) { + return new RippleDrawable(ColorStateList.valueOf(pressedColor), getRippleMask(normalColor), getRippleMask(normalColor)); + } else { + return getStateListDrawable(normalColor, pressedColor); + } + } + + private static Drawable getRippleMask(int color) { + float[] outerRadii = new float[8]; + Arrays.fill(outerRadii, 3); + RoundRectShape r = new RoundRectShape(outerRadii, null, null); + ShapeDrawable shapeDrawable = new ShapeDrawable(r); + shapeDrawable.getPaint().setColor(color); + return shapeDrawable; + } + + private static StateListDrawable getStateListDrawable(int normalColor, int pressedColor) { + StateListDrawable states = new StateListDrawable(); + states.addState(new int[]{android.R.attr.state_pressed}, new ColorDrawable(pressedColor)); + states.addState(new int[]{android.R.attr.state_focused}, new ColorDrawable(pressedColor)); + states.addState(new int[]{android.R.attr.state_activated}, new ColorDrawable(pressedColor)); + states.addState(new int[]{android.R.attr.state_selected}, new ColorDrawable(pressedColor)); + states.addState(new int[]{}, new ColorDrawable(normalColor)); + return states; + } + + public static ColorStateList textSelector(int normalColor, int pressedColor) { + return new ColorStateList( + new int[][]{ + new int[]{android.R.attr.state_pressed}, + new int[]{android.R.attr.state_focused}, + new int[]{android.R.attr.state_activated}, + new int[]{android.R.attr.state_selected}, + new int[]{} + }, + new int[]{ + pressedColor, + pressedColor, + pressedColor, + pressedColor, + normalColor + } + ); + } + + public static int generateTextColor(int color) { + return Color.rgb(255 - Color.red(color), + 255 - Color.green(color), + 255 - Color.blue(color)); + } + + public static int getDarkColor(@ColorInt int color) { + if (color == 0) return color; + float cl = 0.9f; + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + hsv[2] *= cl; + return Color.HSVToColor(hsv); + } + + private static boolean isTablet(Resources resources) { + return (resources.getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; + } + + public static boolean isTablet(Context context) { + return isTablet(context.getResources()); + } + + public static void setMenuCount(@NonNull NavigationView navigationView, @IdRes int itemId, @IdRes int txtId, int count) { + if (navigationView.getMenu().findItem(itemId).getActionView() != null) { + if (navigationView.getMenu().findItem(itemId).getActionView() instanceof TextView) { + setTextViewMenuCounter(navigationView, itemId, count); + return; + } + LinearLayout view = (LinearLayout) navigationView.getMenu().findItem(itemId).getActionView(); + ((TextView) view.findViewById(txtId)).setText(String.format("%s", count)); + } + } + + private static void setTextViewMenuCounter(@NonNull NavigationView navigationView, @IdRes int itemId, int count) { + TextView view = (TextView) navigationView.getMenu().findItem(itemId).getActionView(); + view.setText(String.format("%s", count)); + } + + public static boolean isLandscape(Resources resources) { + return resources.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } + + public static void setFullSpan(@NonNull RecyclerView.ViewHolder holder) { + StaggeredGridLayoutManager.LayoutParams params = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + if (params == null) { + params = new StaggeredGridLayoutManager.LayoutParams(StaggeredGridLayoutManager.LayoutParams.MATCH_PARENT, + StaggeredGridLayoutManager.LayoutParams.WRAP_CONTENT); + } + params.setFullSpan(true); + holder.itemView.setLayoutParams(params); + } + + public static float getDeviceWidth(Context context) { + Point size = new Point(); + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + windowManager.getDefaultDisplay().getSize(size); + return size.x; + } + + public static float getDeviceHeight(Context context) { + Point size = new Point(); + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + windowManager.getDefaultDisplay().getSize(size); + return size.y; + } + + @SuppressWarnings("ConstantConditions") @SuppressLint("PrivateResource") + public static void showTooltip(@NonNull final View view, @StringRes int titleResId, @NonNull final String tag, int gravity) { + if (view != null && view.getContext() != null) { + if (!PrefHelper.getBoolean(tag)) { + new Tooltip.Builder(view) + .setText(titleResId) + .setTypeface(TypeFaceHelper.getTypeface()) + .setTextColor(Color.WHITE) + .setGravity(gravity) + .setPadding(R.dimen.spacing_xs_large) + .setBackgroundColor(ContextCompat.getColor(view.getContext(), R.color.primary)) + .setDismissOnClick(true) + .setCancelable(true) + .setTextStyle(android.support.v7.appcompat.R.style.Base_TextAppearance_AppCompat_Title_Inverse) + .setOnDismissListener(new OnDismissListener() { + @Override public void onDismiss() { + PrefHelper.set(tag, true); + } + }) + .show(); + } + } + } + + @SuppressLint("PrivateResource") public static void showTooltip(@NonNull final View view, @StringRes int titleResId, + @NonNull final String tag) { + showTooltip(view, titleResId, tag, Gravity.BOTTOM); + } + + public static Rect getLayoutPosition(@NonNull View view) { + Rect myViewRect = new Rect(); + view.getGlobalVisibleRect(myViewRect); + return myViewRect; + } + + public static int getWidthFromRecyclerView(@NonNull RecyclerView recyclerView, @NonNull WindowManager windowManager) { + int iconSize = PrefConstant.getFinalSize(recyclerView.getContext()); + int padding = PrefConstant.getGapSize(recyclerView.getResources()); + int count = recyclerView.getAdapter().getItemCount(); + int width = (count * (iconSize + padding)) + iconSize; + DisplayMetrics metrics = new DisplayMetrics(); + windowManager.getDefaultDisplay().getMetrics(metrics); + Logger.e(width <= metrics.widthPixels); + return width <= metrics.widthPixels ? width : metrics.widthPixels; + } +} diff --git a/app/src/main/java/com/fastaccess/provider/analytics/Analytics.java b/app/src/main/java/com/fastaccess/provider/analytics/Analytics.java new file mode 100644 index 0000000..dcd814c --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/analytics/Analytics.java @@ -0,0 +1,34 @@ +package com.fastaccess.provider.analytics; + +import android.os.Bundle; + +import com.fastaccess.App; +import com.google.firebase.analytics.FirebaseAnalytics; + +/** + * Created by Kosh on 29 May 2016, 1:25 AM + */ + +public class Analytics { + + public static void logEvent() { + Bundle bundle = new Bundle(); + bundle.putString(FirebaseAnalytics.Param.ITEM_CATEGORY, getFileName()); + bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, getCurrentMethodName()); + App.getInstance().getFirebaseAnalytics().logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle); + } + + private static String getCurrentMethodName() { + try { + return Thread.currentThread().getStackTrace()[4].getMethodName() + "()"; + } catch (Exception ignored) {} + return Analytics.class.getSimpleName(); + } + + private static String getFileName() { + try { + return Thread.currentThread().getStackTrace()[4].getFileName(); + } catch (Exception ignored) {} + return Analytics.class.getSimpleName(); + } +} diff --git a/app/src/main/java/com/fastaccess/provider/icon/IconCache.java b/app/src/main/java/com/fastaccess/provider/icon/IconCache.java new file mode 100644 index 0000000..349e4fb --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/icon/IconCache.java @@ -0,0 +1,415 @@ +package com.fastaccess.provider.icon; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; +import android.support.v4.content.res.ResourcesCompat; +import android.text.TextUtils; +import android.util.DisplayMetrics; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; + +import java.util.HashMap; + +/** + * Cache of application icons. Icons can be made from any thread. + */ +public class IconCache { + private static final int INITIAL_ICON_CACHE_CAPACITY = 50; + + private static class CacheEntry { + public Bitmap icon; + public String title; + } + + private final Bitmap mDefaultIcon; + private final Context mContext; + private final PackageManager mPackageManager; + private final HashMap mCache = new HashMap<>(INITIAL_ICON_CACHE_CAPACITY); + private int mIconDpi; + private static int sIconWidth = -1; + private static int sIconHeight = -1; + public static int sIconTextureWidth = -1; + public static int sIconTextureHeight = -1; + private static final Paint sBlurPaint = new Paint(); + private static final Paint sGlowColorPressedPaint = new Paint(); + private static final Paint sGlowColorFocusedPaint = new Paint(); + private static final Paint sDisabledPaint = new Paint(); + private static final Rect sOldBounds = new Rect(); + private static final Canvas sCanvas = new Canvas(); + private IconPackHelper mIconPackHelper; + + static { + sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, Paint.FILTER_BITMAP_FLAG)); + } + + public IconCache(Context context) { + ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + mContext = context; + mPackageManager = context.getPackageManager(); + mIconDpi = activityManager.getLauncherLargeIconDensity(); + mDefaultIcon = makeDefaultIcon(); + mIconPackHelper = new IconPackHelper(context); + loadIconPack(); + + } + + public void getTitleAndIcon(AppsModel application, ResolveInfo info, HashMap labelCache) { + synchronized (mCache) { + CacheEntry entry = cacheLocked(application.getComponentName(), info, labelCache); + application.setAppName(entry.title); + application.setBitmap(entry.icon); + } + } + + private void loadIconPack() { + mIconPackHelper.unloadIconPack(); + String iconPack = PrefHelper.getString(PrefConstant.ICON_PACK); + if (!TextUtils.isEmpty(iconPack) && !mIconPackHelper.loadIconPack(iconPack)) { + PrefHelper.set(PrefConstant.ICON_PACK, ""); + } + } + + public Drawable getFullResDefaultActivityIcon() { + return getFullResIcon(Resources.getSystem(), + android.R.mipmap.sym_def_app_icon); + } + + public Drawable getFullResIcon(Resources resources, int iconId) { + Drawable d; + try { + d = ResourcesCompat.getDrawableForDensity(resources, iconId, mIconDpi, mContext.getTheme()); + } catch (Resources.NotFoundException e) { + d = null; + } + + return (d != null) ? d : getFullResDefaultActivityIcon(); + } + + public Drawable getFullResIcon(String packageName, int iconId) { + Resources resources; + try { + resources = mPackageManager.getResourcesForApplication(packageName); + } catch (PackageManager.NameNotFoundException e) { + resources = null; + } + if (resources != null) { + if (iconId != 0) { + return getFullResIcon(resources, iconId); + } + } + return getFullResDefaultActivityIcon(); + } + + public Drawable getFullResIcon(ResolveInfo info) { + return getFullResIcon(info.activityInfo); + } + + public Drawable getFullResIcon(ActivityInfo info) { + Resources resources; + try { + resources = mPackageManager.getResourcesForApplication(info.applicationInfo); + } catch (PackageManager.NameNotFoundException e) { + resources = null; + } + if (resources != null) { + int iconId = 0; + if (mIconPackHelper != null && mIconPackHelper.isIconPackLoaded()) { + iconId = mIconPackHelper.getResourceIdForActivityIcon(info); + if (iconId != 0) { + return getFullResIcon(mIconPackHelper.getIconPackResources(), iconId); + } + } + iconId = info.getIconResource(); + if (iconId != 0) { + return getFullResIcon(resources, iconId); + } + } + return getFullResDefaultActivityIcon(); + } + + private Bitmap makeDefaultIcon() { + Drawable d = getFullResDefaultActivityIcon(); + Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1), + Math.max(d.getIntrinsicHeight(), 1), + Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + d.setBounds(0, 0, b.getWidth(), b.getHeight()); + d.draw(c); + c.setBitmap(null); + return b; + } + + public void remove(ComponentName componentName) { + synchronized (mCache) { + mCache.remove(componentName); + } + } + + public void flush() { + synchronized (mCache) { + mCache.clear(); + } + } + + public Bitmap getIcon(Intent intent) { + synchronized (mCache) { + final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0); + ComponentName component = intent.getComponent(); + + if (resolveInfo == null || component == null) { + return mDefaultIcon; + } + + CacheEntry entry = cacheLocked(component, resolveInfo, null); + return entry.icon; + } + } + + public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo, HashMap labelCache) { + synchronized (mCache) { + if (resolveInfo == null || component == null) { + return null; + } + CacheEntry entry = cacheLocked(component, resolveInfo, labelCache); + return entry.icon; + } + } + + public boolean isDefaultIcon(Bitmap icon) { + return mDefaultIcon == icon; + } + + private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info, HashMap labelCache) { + CacheEntry entry = mCache.get(componentName); + if (entry == null) { + entry = new CacheEntry(); + mCache.put(componentName, entry); + ComponentName key; + if (info.activityInfo != null) { + key = new ComponentName(info.activityInfo.packageName, info.activityInfo.name); + } else { + key = new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name); + } + if (labelCache != null && labelCache.containsKey(key)) { + entry.title = labelCache.get(key).toString(); + } else { + entry.title = info.loadLabel(mPackageManager).toString(); + if (labelCache != null) { + labelCache.put(key, entry.title); + } + } + if (entry.title == null) { + entry.title = info.activityInfo.name; + } + + Drawable icon = getFullResIcon(info); + if (mIconPackHelper.isIconPackLoaded() && (mIconPackHelper.getResourceIdForActivityIcon(info.activityInfo) == 0)) { + entry.icon = createIconBitmap(icon, mContext, mIconPackHelper.getIconBack(), mIconPackHelper.getIconMask(), mIconPackHelper + .getIconUpon(), mIconPackHelper.getIconScale()); + } else { + entry.icon = createIconBitmap(icon, mContext); + } + } + return entry; + } + + public Bitmap getIcon(String packageName, String activityInfoName) { + ComponentName componentName = new ComponentName(packageName, activityInfoName); + if (mCache.get(componentName) != null) { + return mCache.get(componentName).icon; + } else { + Intent intent = new Intent(); + intent.setComponent(new ComponentName(packageName, activityInfoName)); + return getIcon(intent); + } + } + + public HashMap getAllIcons() { + synchronized (mCache) { + HashMap set = new HashMap<>(); + for (ComponentName cn : mCache.keySet()) { + final CacheEntry e = mCache.get(cn); + set.put(cn, e.icon); + } + return set; + } + } + + public static Bitmap createIconBitmap(Bitmap icon, Context context) { + int textureWidth = sIconTextureWidth; + int textureHeight = sIconTextureHeight; + int sourceWidth = icon.getWidth(); + int sourceHeight = icon.getHeight(); + if (sourceWidth > textureWidth && sourceHeight > textureHeight) { + // Icon is bigger than it should be; clip it (solves the GB->ICS migration case) + return Bitmap.createBitmap(icon, + (sourceWidth - textureWidth) / 2, + (sourceHeight - textureHeight) / 2, + textureWidth, textureHeight); + } else if (sourceWidth == textureWidth && sourceHeight == textureHeight) { + // Icon is the right size, no need to change it + return icon; + } else { + // Icon is too small, render to a larger bitmap + final Resources resources = context.getResources(); + return createIconBitmap(new BitmapDrawable(resources, icon), context); + } + } + + public static Bitmap createIconBitmap(Drawable icon, Context context) { + synchronized (sCanvas) { // we share the statics :-( + if (sIconWidth == -1) { + initStatics(context); + } + + int width = sIconWidth; + int height = sIconHeight; + + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } else if (icon instanceof BitmapDrawable) { + // Ensure the bitmap has a density. + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics()); + } + } + int sourceWidth = icon.getIntrinsicWidth(); + int sourceHeight = icon.getIntrinsicHeight(); + if (sourceWidth > 0 && sourceHeight > 0) { + // Scale the icon proportionally to the icon dimensions + final float ratio = (float) sourceWidth / sourceHeight; + if (sourceWidth > sourceHeight) { + height = (int) (width / ratio); + } else if (sourceHeight > sourceWidth) { + width = (int) (height * ratio); + } + } + + // no intrinsic size --> use default size + int textureWidth = sIconTextureWidth; + int textureHeight = sIconTextureHeight; + final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888); + final Canvas canvas = sCanvas; + canvas.setBitmap(bitmap); + final int left = (textureWidth - width) / 2; + final int top = (textureHeight - height) / 2; + sOldBounds.set(icon.getBounds()); + icon.setBounds(left, top, left + width, top + height); + icon.draw(canvas); + icon.setBounds(sOldBounds); + canvas.setBitmap(null); + return bitmap; + } + } + + public static Bitmap createIconBitmap(Drawable icon, Context context, Drawable iconBack, Drawable iconMask, Drawable iconUpon, float scale) { + synchronized (sCanvas) { // we share the statics :-( + if (sIconWidth == -1) { + initStatics(context); + } + int width = sIconWidth; + int height = sIconHeight; + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } else if (icon instanceof BitmapDrawable) { + // Ensure the bitmap has a density. + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics()); + } + } + int sourceWidth = icon.getIntrinsicWidth(); + int sourceHeight = icon.getIntrinsicHeight(); + if (sourceWidth > 0 && sourceHeight > 0) { + // Scale the icon proportionally to the icon dimensions + final float ratio = (float) sourceWidth / sourceHeight; + if (sourceWidth > sourceHeight) { + height = (int) (width / ratio); + } else if (sourceHeight > sourceWidth) { + width = (int) (height * ratio); + } + } + + // no intrinsic size --> use default size + int textureWidth = sIconTextureWidth; + int textureHeight = sIconTextureHeight; + + Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, + Bitmap.Config.ARGB_8888); + final Canvas canvas = sCanvas; + canvas.setBitmap(bitmap); + final int left = (textureWidth - width) / 2; + final int top = (textureHeight - height) / 2; + sOldBounds.set(icon.getBounds()); + icon.setBounds(left, top, left + width, top + height); + canvas.save(); + canvas.scale(scale, scale, width / 2, height / 2); + icon.draw(canvas); + canvas.restore(); + if (iconMask != null) { + iconMask.setBounds(icon.getBounds()); + ((BitmapDrawable) iconMask).getPaint().setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + iconMask.draw(canvas); + } + if (iconBack != null) { + canvas.setBitmap(null); + Bitmap finalBitmap = Bitmap.createBitmap(textureWidth, textureHeight, Bitmap.Config.ARGB_8888); + canvas.setBitmap(finalBitmap); + iconBack.setBounds(icon.getBounds()); + iconBack.draw(canvas); + canvas.drawBitmap(bitmap, null, icon.getBounds(), null); + bitmap = finalBitmap; + } + if (iconUpon != null) { + iconUpon.draw(canvas); + } + icon.setBounds(sOldBounds); + canvas.setBitmap(null); + + return bitmap; + } + } + + private static void initStatics(Context context) { + final Resources resources = context.getResources(); + final DisplayMetrics metrics = resources.getDisplayMetrics(); + final float density = metrics.density; + sIconWidth = sIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size); + sIconTextureWidth = sIconTextureHeight = sIconWidth; + sBlurPaint.setMaskFilter(new BlurMaskFilter(5 * density, BlurMaskFilter.Blur.NORMAL)); + sGlowColorPressedPaint.setColor(0xffffc300); + sGlowColorFocusedPaint.setColor(0xffff8e00); + ColorMatrix cm = new ColorMatrix(); + cm.setSaturation(0.2f); + sDisabledPaint.setColorFilter(new ColorMatrixColorFilter(cm)); + sDisabledPaint.setAlpha(0x88); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/provider/icon/IconPackAdapter.java b/app/src/main/java/com/fastaccess/provider/icon/IconPackAdapter.java new file mode 100644 index 0000000..743ee60 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/icon/IconPackAdapter.java @@ -0,0 +1,99 @@ +package com.fastaccess.provider.icon; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import com.fastaccess.R; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.provider.icon.model.IconPackInfo; +import com.fastaccess.ui.widgets.FontRadioButton; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.ForegroundImageView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Map; + +import butterknife.BindView; +import butterknife.ButterKnife; + +/** + * Created by Kosh on 13/12/15 1:21 AM + */ +public class IconPackAdapter extends BaseAdapter { + + private ArrayList mSupportedPackages; + private String mCurrentIconPack; + private int mCurrentIconPackPosition = -1; + private boolean isPickIcon; + + public IconPackAdapter(Context ctx, Map supportedPackages, boolean isPickIcon) { + mSupportedPackages = new ArrayList<>(supportedPackages.values()); + this.isPickIcon = isPickIcon; + Collections.sort(mSupportedPackages, new Comparator() { + @Override public int compare(IconPackInfo lhs, IconPackInfo rhs) { + return lhs.label.toString().compareToIgnoreCase(rhs.label.toString()); + } + }); + String defaultLabel = ctx.getString(R.string.default_theme); + Drawable icon = ContextCompat.getDrawable(ctx, R.mipmap.ic_launcher); + mSupportedPackages.add(0, new IconPackInfo(defaultLabel, icon, "")); + mCurrentIconPack = PrefHelper.getString(PrefConstant.ICON_PACK); + } + + @Override public int getCount() { + return mSupportedPackages.size(); + } + + @Override public String getItem(int position) { + return mSupportedPackages.get(position).packageName; + } + + @Override public long getItemId(int position) { + return 0; + } + + public boolean isCurrentIconPack(int position) { + return mCurrentIconPackPosition == position; + } + + @Override public View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + ViewHolder viewHolder; + if (view == null) { + view = LayoutInflater.from(parent.getContext()).inflate(R.layout.icon_pack_layout, parent, false); + viewHolder = new ViewHolder(view); + view.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) view.getTag(); + } + IconPackInfo info = mSupportedPackages.get(position); + viewHolder.title.setText(info.label); + viewHolder.icon.setImageDrawable(info.icon); + if (!isPickIcon) { + boolean isCurrentIconPack = info.packageName.equals(mCurrentIconPack); + viewHolder.radio.setChecked(isCurrentIconPack); + if (isCurrentIconPack) { + mCurrentIconPackPosition = position; + } + } else { + viewHolder.radio.setVisibility(View.GONE); + } + return view; + } + + static class ViewHolder { + @BindView(R.id.icon) ForegroundImageView icon; + @BindView(R.id.title) FontTextView title; + @BindView(R.id.radio) FontRadioButton radio; + + ViewHolder(View view) {ButterKnife.bind(this, view);} + } +} diff --git a/app/src/main/java/com/fastaccess/provider/icon/IconPackHelper.java b/app/src/main/java/com/fastaccess/provider/icon/IconPackHelper.java new file mode 100644 index 0000000..56fe5d2 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/icon/IconPackHelper.java @@ -0,0 +1,455 @@ +package com.fastaccess.provider.icon; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ComponentName; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.support.v4.app.Fragment; +import android.text.TextUtils; +import android.widget.Toast; + +import com.fastaccess.App; +import com.fastaccess.R; +import com.fastaccess.data.dao.ThemePackEventModel; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.provider.icon.model.IconPackInfo; + +import org.greenrobot.eventbus.EventBus; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class IconPackHelper { + public final static int PICK_ICON = 2001; + static final String ICON_MASK_TAG = "iconmask"; + static final String ICON_BACK_TAG = "iconback"; + static final String ICON_UPON_TAG = "iconupon"; + static final String ICON_SCALE_TAG = "scale"; + final static String PLAY_STORE_PACKAGENAME = "com.android.vending"; + final static String PLAY_STORE_SEARCH_URI = "market://search?q=icon+pack"; + + public final static String[] sSupportedActions = new String[]{ + "org.adw.launcher.THEMES", + "com.gau.go.launcherex.theme" + }; + + public static final String[] sSupportedCategories = new String[]{ + "com.fede.launcher.THEME_ICONPACK", + "com.anddoes.launcher.THEME", + "com.teslacoilsw.launcher.THEME" + }; + + private Map mIconPackResources; + private final Context mContext; + private String mLoadedIconPackName; + private Resources mLoadedIconPackResource; + private Drawable mIconBack, mIconUpon, mIconMask; + private float mIconScale; + + public Drawable getIconBack() { + return mIconBack; + } + + public Drawable getIconMask() { + return mIconMask; + } + + public Drawable getIconUpon() { + return mIconUpon; + } + + public float getIconScale() { + return mIconScale; + } + + public IconPackHelper(Context context) { + mContext = context; + mIconPackResources = new HashMap<>(); + } + + private Drawable getDrawableForName(String name) { + if (isIconPackLoaded()) { + String item = mIconPackResources.get(name); + if (!TextUtils.isEmpty(item)) { + int id = getResourceIdForDrawable(item); + if (id != 0) { + return mLoadedIconPackResource.getDrawable(id); + } + } + } + return null; + } + + public static Map getSupportedPackages(Context context) { + Intent i = new Intent(); + Map packages = new HashMap<>(); + PackageManager packageManager = context.getPackageManager(); + for (String action : sSupportedActions) { + i.setAction(action); + for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { + IconPackInfo info = new IconPackInfo(r, packageManager); + packages.put(r.activityInfo.packageName, info); + } + } + i = new Intent(Intent.ACTION_MAIN); + for (String category : sSupportedCategories) { + i.addCategory(category); + for (ResolveInfo r : packageManager.queryIntentActivities(i, 0)) { + IconPackInfo info = new IconPackInfo(r, packageManager); + packages.put(r.activityInfo.packageName, info); + } + i.removeCategory(category); + } + return packages; + } + + private static void loadResourcesFromXmlParser(XmlPullParser parser, Map iconPackResources) throws XmlPullParserException, + IOException { + int eventType = parser.getEventType(); + do { + + if (eventType != XmlPullParser.START_TAG) { + continue; + } + if (parser.getName().equalsIgnoreCase(ICON_MASK_TAG) || + parser.getName().equalsIgnoreCase(ICON_BACK_TAG) || + parser.getName().equalsIgnoreCase(ICON_UPON_TAG)) { + List icons = new ArrayList<>(); + icons.add(parser.getAttributeValue(null, "img")); + for (int i = 0; i < 10; i++) { + icons.add(parser.getAttributeValue(null, "img" + i)); + } + for (String icon : icons) { + if (icon == null) { + if (parser.getAttributeCount() > 0) { + for (int count = 0; count < parser.getAttributeCount(); count++) { + icon = parser.getAttributeValue(count); + iconPackResources.put(parser.getName().toLowerCase(), icon); + } + } + } + } + continue; + } + if (parser.getName().equalsIgnoreCase(ICON_SCALE_TAG)) { + String factor = parser.getAttributeValue(null, "factor"); + if (factor == null) { + if (parser.getAttributeCount() == 1) { + factor = parser.getAttributeValue(0); + } + } + iconPackResources.put(parser.getName().toLowerCase(), factor); + continue; + } + + if (!parser.getName().equalsIgnoreCase("item")) { + continue; + } + + String component = parser.getAttributeValue(null, "component"); + String drawable = parser.getAttributeValue(null, "drawable"); + + // Validate component/drawable exist + if (TextUtils.isEmpty(component) || TextUtils.isEmpty(drawable)) { + continue; + } + + // Validate format/length of component + if (!component.startsWith("ComponentInfo{") || !component.endsWith("}") + || component.length() < 16) { + continue; + } + + // Sanitize stored value + component = component.substring(14, component.length() - 1).toLowerCase(); + + ComponentName name = null; + if (!component.contains("/")) { + // Package icon reference + iconPackResources.put(component, drawable); + } else { + name = ComponentName.unflattenFromString(component); + if (name != null) { + iconPackResources.put(name.getPackageName(), drawable); + iconPackResources.put(name.getPackageName() + "." + name.getClassName(), drawable); + } + } + } while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT); + } + + private static void loadApplicationResources(Context context, Map iconPackResources, String packageName) { + Field[] drawableItems = null; + try { + Context appContext = context.createPackageContext(packageName, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + drawableItems = Class.forName(packageName + ".R$drawable", + true, appContext.getClassLoader()).getFields(); + } catch (Exception e) { + return; + } + for (Field f : drawableItems) { + String name = f.getName(); + + String icon = name.toLowerCase(); + name = name.replaceAll("_", "."); + + iconPackResources.put(name, icon); + + int activityIndex = name.lastIndexOf("."); + if (activityIndex <= 0 || activityIndex == name.length() - 1) { + continue; + } + + String iconPackage = name.substring(0, activityIndex); + if (TextUtils.isEmpty(iconPackage)) { + continue; + } + iconPackResources.put(iconPackage, icon); + + String iconActivity = name.substring(activityIndex + 1); + if (TextUtils.isEmpty(iconActivity)) { + continue; + } + iconPackResources.put(iconPackage + "." + iconActivity, icon); + } + } + + public boolean loadIconPack(String packageName) { + mIconPackResources = getIconPackResources(mContext, packageName); + Resources res; + try { + res = mContext.getPackageManager().getResourcesForApplication(packageName); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return false; + } + mLoadedIconPackResource = res; + mLoadedIconPackName = packageName; + mIconBack = getDrawableForName(ICON_BACK_TAG); + mIconMask = getDrawableForName(ICON_MASK_TAG); + mIconUpon = getDrawableForName(ICON_UPON_TAG); + String scale = mIconPackResources.get(ICON_SCALE_TAG); + if (scale != null) { + try { + mIconScale = Float.valueOf(scale); + } catch (NumberFormatException ignored) {} + } + return true; + } + + public static Map getIconPackResources(Context context, String packageName) { + if (TextUtils.isEmpty(packageName)) { + return null; + } + + Resources res = null; + try { + res = context.getPackageManager().getResourcesForApplication(packageName); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + return null; + } + + XmlPullParser parser = null; + InputStream inputStream = null; + Map iconPackResources = new HashMap(); + + try { + inputStream = res.getAssets().open("appfilter.xml"); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + parser = factory.newPullParser(); + parser.setInput(inputStream, "UTF-8"); + } catch (Exception e) { + // Catch any exception since we want to fall back to parsing the xml/ + // resource in all cases + int resId = res.getIdentifier("appfilter", "xml", packageName); + if (resId != 0) { + parser = res.getXml(resId); + } + } + + if (parser != null) { + try { + loadResourcesFromXmlParser(parser, iconPackResources); + return iconPackResources; + } catch (XmlPullParserException | IOException e) { + e.printStackTrace(); + } finally { + // Cleanup resources + if (parser instanceof XmlResourceParser) { + ((XmlResourceParser) parser).close(); + } + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ignored) {} + } + } + } + + // Application uses a different theme format (most likely launcher pro) + int arrayId = res.getIdentifier("theme_iconpack", "array", packageName); + if (arrayId == 0) { + arrayId = res.getIdentifier("icon_pack", "array", packageName); + } + + if (arrayId != 0) { + String[] iconPack = res.getStringArray(arrayId); + for (String entry : iconPack) { + + if (TextUtils.isEmpty(entry)) { + continue; + } + + String icon = entry.toLowerCase(); + entry = entry.replaceAll("_", "."); + + iconPackResources.put(entry, icon); + + int activityIndex = entry.lastIndexOf("."); + if (activityIndex <= 0 || activityIndex == entry.length() - 1) { + continue; + } + + String iconPackage = entry.substring(0, activityIndex); + if (TextUtils.isEmpty(iconPackage)) { + continue; + } + iconPackResources.put(iconPackage, icon); + + String iconActivity = entry.substring(activityIndex + 1); + if (TextUtils.isEmpty(iconActivity)) { + continue; + } + iconPackResources.put(iconPackage + "." + iconActivity, icon); + } + } else { + loadApplicationResources(context, iconPackResources, packageName); + } + return iconPackResources; + } + + public void unloadIconPack() { + mLoadedIconPackResource = null; + mLoadedIconPackName = null; + mIconPackResources = null; + mIconMask = null; + mIconBack = null; + mIconUpon = null; + mIconScale = 1f; + } + + public static void pickIconPack(final Fragment context, final boolean pickIcon) { + Map supportedPackages = getSupportedPackages(context.getContext()); + AlertDialog.Builder builder = new AlertDialog.Builder(context.getContext(), R.style.AlertDialogStyle); + if (supportedPackages.isEmpty()) { + builder.setTitle(context.getString(R.string.get_themes)); + builder.setMessage(R.string.no_icon_packs) + .setPositiveButton(R.string.get_themes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(PLAY_STORE_SEARCH_URI)); + context.startActivity(intent); + } + }).setNegativeButton(R.string.cancel, null) + .show(); + return; + } + final IconPackAdapter adapter = new IconPackAdapter(context.getContext(), supportedPackages, pickIcon); + if (!pickIcon) { + builder.setTitle(R.string.choose_theme_pack); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int position) { + if (adapter.isCurrentIconPack(position)) { + return; + } + String selectedPackage = adapter.getItem(position); + PrefHelper.set(PrefConstant.ICON_PACK, selectedPackage); + App.getInstance().flushIconPack(); + EventBus.getDefault().post(new ThemePackEventModel()); + } + }).setPositiveButton(context.getString(R.string.get_themes), new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int which) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(PLAY_STORE_SEARCH_URI)); + context.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } else { + builder.setTitle(R.string.choose_icon); + builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int which) { + if (which == 0) { + Intent intent = new Intent(); + intent.putExtra("default", true); + context.onActivityResult(PICK_ICON, Activity.RESULT_CANCELED, intent); + } else { + String selectedPackage = adapter.getItem(which); + try { + Intent i = new Intent(); + i.setPackage(selectedPackage); + i.setAction("org.adw.launcher.icons.ACTION_PICK_ICON"); + i.putExtra("package", selectedPackage); + context.startActivityForResult(i, PICK_ICON); + } catch (Exception e) { + Toast.makeText(context.getContext(), context.getString(R.string.not_support_icon_theme), Toast.LENGTH_LONG).show(); + } + } + } + }); + } + builder.show(); + } + + boolean isIconPackLoaded() { + return mLoadedIconPackResource != null && + mLoadedIconPackName != null && + mIconPackResources != null; + } + + private int getResourceIdForDrawable(String resource) { + return mLoadedIconPackResource.getIdentifier(resource, "drawable", mLoadedIconPackName); + } + + public Resources getIconPackResources() { + return mLoadedIconPackResource; + } + + public int getResourceIdForActivityIcon(ActivityInfo info) { + String drawable = mIconPackResources.get(info.packageName.toLowerCase() + + "." + info.name.toLowerCase()); + if (drawable == null) { + // Icon pack doesn't have an icon for the activity, fallback to package icon + drawable = mIconPackResources.get(info.packageName.toLowerCase()); + if (drawable == null) { + return 0; + } + } + return getResourceIdForDrawable(drawable); + } + + +} diff --git a/app/src/main/java/com/fastaccess/provider/icon/model/IconPackInfo.java b/app/src/main/java/com/fastaccess/provider/icon/model/IconPackInfo.java new file mode 100644 index 0000000..e4e155b --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/icon/model/IconPackInfo.java @@ -0,0 +1,28 @@ +package com.fastaccess.provider.icon.model; + +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; + +/** + * Created by Kosh on 13/12/15 1:21 AM + */ +public class IconPackInfo { + public String packageName; + public CharSequence label; + public Drawable icon; + + private IconPackInfo() {} + + public IconPackInfo(ResolveInfo r, PackageManager packageManager) { + packageName = r.activityInfo.packageName; + icon = r.loadIcon(packageManager); + label = r.loadLabel(packageManager); + } + + public IconPackInfo(String label, Drawable icon, String packageName) { + this.label = label; + this.icon = icon; + this.packageName = packageName; + } +} diff --git a/app/src/main/java/com/fastaccess/provider/loader/DeviceAppsLoader.java b/app/src/main/java/com/fastaccess/provider/loader/DeviceAppsLoader.java new file mode 100644 index 0000000..24f78bc --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/loader/DeviceAppsLoader.java @@ -0,0 +1,110 @@ +package com.fastaccess.provider.loader; + + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.support.v4.content.AsyncTaskLoader; + +import com.fastaccess.App; +import com.fastaccess.BuildConfig; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.helper.AppHelper; +import com.fastaccess.provider.icon.IconCache; +import com.fastaccess.provider.receiver.ApplicationsReceiver; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DeviceAppsLoader extends AsyncTaskLoader> { + private ApplicationsReceiver mAppsObserver; + private final PackageManager packageManager; + private List appsModelList; + + public DeviceAppsLoader(Context ctx) { + super(ctx); + packageManager = getContext().getPackageManager(); + } + + @Override public List loadInBackground() { + try { + List entries = new ArrayList<>(); + Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); + mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); + List list = packageManager.queryIntentActivities(mainIntent, 0); + if (list == null || list.isEmpty()) { + return entries; + } + Collections.sort(list, new ResolveInfo.DisplayNameComparator(packageManager)); + String appPackage = BuildConfig.APPLICATION_ID; + IconCache iconCache = App.getInstance().getIconCache(); + for (ResolveInfo resolveInfo : list) { + if (!resolveInfo.activityInfo.applicationInfo.packageName.equalsIgnoreCase(appPackage)) { + AppsModel model = new AppsModel(); + model.setPackageName(resolveInfo.activityInfo.applicationInfo.packageName); + model.setActivityInfoName(resolveInfo.activityInfo.name); + iconCache.getTitleAndIcon(model, resolveInfo, null); + entries.add(model); + } + } + return entries; + } catch (Exception e) {//catching TransactionTooLargeException, + e.printStackTrace(); + return AppHelper.getInstalledPackages(getContext()); + } + } + + @Override public void deliverResult(List apps) { + if (isReset()) { + if (apps != null) { + return; + } + } + appsModelList = apps; + if (isStarted()) { + super.deliverResult(apps); + } + } + + @Override protected void onStartLoading() { + if (appsModelList != null && !appsModelList.isEmpty()) { + deliverResult(appsModelList); + } + if (mAppsObserver == null) { + mAppsObserver = new ApplicationsReceiver(this); + } + if (takeContentChanged()) { + forceLoad(); + } else if (appsModelList == null) { + forceLoad(); + } + + + } + + @Override protected void onStopLoading() { + cancelLoad(); + } + + @Override protected void onReset() { + onStopLoading(); + if (appsModelList != null) { + appsModelList = null; + } + if (mAppsObserver != null) { + getContext().unregisterReceiver(mAppsObserver); + mAppsObserver = null; + } + } + + @Override public void onCanceled(List apps) { + super.onCanceled(apps); + } + + @Override public void forceLoad() { + if (appsModelList != null) appsModelList.clear(); + super.forceLoad(); + } +} diff --git a/app/src/main/java/com/fastaccess/provider/loader/FoldersLoader.java b/app/src/main/java/com/fastaccess/provider/loader/FoldersLoader.java new file mode 100644 index 0000000..04f807a --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/loader/FoldersLoader.java @@ -0,0 +1,70 @@ +package com.fastaccess.provider.loader; + +import android.content.Context; +import android.support.v4.content.AsyncTaskLoader; + +import com.fastaccess.data.dao.FolderModel; + +import java.util.List; + +/** + * Created by Kosh on 11 Oct 2016, 7:37 PM + */ + +public class FoldersLoader extends AsyncTaskLoader> { + + private List folderModels; + + public FoldersLoader(Context context) { + super(context); + } + + @Override public List loadInBackground() { + return FolderModel.getFolders(); + } + + @Override public void deliverResult(List folders) { + if (isReset()) { + if (folders != null) { + return; + } + } + folderModels = folders; + if (isStarted()) { + super.deliverResult(folders); + } + } + + @Override protected void onStartLoading() { + if (folderModels != null) { + deliverResult(folderModels); + } + if (takeContentChanged()) { + forceLoad(); + } else if (folderModels == null) { + forceLoad(); + } + + + } + + @Override protected void onStopLoading() { + cancelLoad(); + } + + @Override protected void onReset() { + onStopLoading(); + if (folderModels != null) { + + folderModels = null; + } + } + + @Override public void onCanceled(List apps) { + super.onCanceled(apps); + } + + @Override public void forceLoad() { + super.forceLoad(); + } +} diff --git a/app/src/main/java/com/fastaccess/provider/loader/SelectedAppsLoader.java b/app/src/main/java/com/fastaccess/provider/loader/SelectedAppsLoader.java new file mode 100644 index 0000000..2a0c971 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/loader/SelectedAppsLoader.java @@ -0,0 +1,104 @@ +package com.fastaccess.provider.loader; + + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.support.v4.content.AsyncTaskLoader; + +import com.fastaccess.App; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.provider.icon.IconCache; +import com.fastaccess.provider.receiver.ApplicationsReceiver; + +import java.util.ArrayList; +import java.util.List; + +public class SelectedAppsLoader extends AsyncTaskLoader> { + private ApplicationsReceiver mAppsObserver; + private final PackageManager packageManager; + private List appsModelList; + private long folderId = -1; + + public SelectedAppsLoader(Context ctx) { + super(ctx); + packageManager = getContext().getPackageManager(); + } + + public SelectedAppsLoader(Context ctx, long folderId) { + super(ctx); + this.folderId = folderId; + packageManager = getContext().getPackageManager(); + } + + @Override public List loadInBackground() { + List savedApps = folderId == -1 ? AppsModel.getApps() : AppsModel.getApps(folderId); + if (savedApps == null || savedApps.isEmpty()) return new ArrayList<>(); + IconCache iconCache = App.getInstance().getIconCache(); + for (AppsModel model : savedApps) { + Intent intent = new Intent(); + intent.setComponent(new ComponentName(model.getPackageName(), model.getActivityInfoName())); + ResolveInfo resolveInfo = packageManager.resolveActivity(intent, 0); + if (resolveInfo != null) { + iconCache.getTitleAndIcon(model, resolveInfo, null); + } else { + model.delete();//app is uninstalled! + } + } + return savedApps; + } + + @Override public void deliverResult(List apps) { + if (isReset()) { + if (apps != null) { + return; + } + } + appsModelList = apps; + if (isStarted()) { + super.deliverResult(apps); + } + } + + @Override protected void onStartLoading() { + if (appsModelList != null && !appsModelList.isEmpty()) { + deliverResult(appsModelList); + } + if (mAppsObserver == null) { + mAppsObserver = new ApplicationsReceiver(this); + } + if (takeContentChanged()) { + forceLoad(); + } else if (appsModelList == null) { + forceLoad(); + } + + + } + + @Override protected void onStopLoading() { + cancelLoad(); + } + + @Override protected void onReset() { + onStopLoading(); + if (appsModelList != null) { + appsModelList = null; + } + if (mAppsObserver != null) { + getContext().unregisterReceiver(mAppsObserver); + mAppsObserver = null; + } + } + + @Override public void onCanceled(List apps) { + super.onCanceled(apps); + } + + @Override public void forceLoad() { + if (appsModelList != null) appsModelList.clear(); + super.forceLoad(); + } +} diff --git a/app/src/main/java/com/fastaccess/provider/push/PushNotification.java b/app/src/main/java/com/fastaccess/provider/push/PushNotification.java new file mode 100644 index 0000000..ca31c6d --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/push/PushNotification.java @@ -0,0 +1,19 @@ +package com.fastaccess.provider.push; + +import com.fastaccess.R; +import com.fastaccess.helper.NotificationHelper; +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +/** + * Created by Kosh on 24 May 2016, 6:56 PM + */ +public class PushNotification extends FirebaseMessagingService { + @Override public void onMessageReceived(RemoteMessage remoteMessage) { + super.onMessageReceived(remoteMessage); + if (remoteMessage.getNotification() != null) { + NotificationHelper.notifyShort(this, remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(), R.drawable + .ic_fa); + } + } +} diff --git a/app/src/main/java/com/fastaccess/provider/receiver/ApplicationsReceiver.java b/app/src/main/java/com/fastaccess/provider/receiver/ApplicationsReceiver.java new file mode 100644 index 0000000..3be3004 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/receiver/ApplicationsReceiver.java @@ -0,0 +1,39 @@ +package com.fastaccess.provider.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.support.v4.content.Loader; + +import static android.content.Intent.ACTION_PACKAGE_REMOVED; +import static android.content.Intent.ACTION_UNINSTALL_PACKAGE; + +/** + * Created by kosh on 18 Oct 2016, 9:33 PM + */ +public class ApplicationsReceiver extends BroadcastReceiver { + + private final static IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); + + static { + filter.addAction(ACTION_PACKAGE_REMOVED); + filter.addAction(Intent.ACTION_PACKAGE_CHANGED); + filter.addAction(Intent.ACTION_INSTALL_PACKAGE); + filter.addAction(ACTION_UNINSTALL_PACKAGE); + filter.addDataScheme("package"); + } + + private Loader loader; + + public ApplicationsReceiver(Loader loader) { + this.loader = loader; + this.loader.getContext().registerReceiver(this, filter); + } + + @Override public void onReceive(Context context, Intent intent) { + boolean isReplacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + if (isReplacing) return; + if (loader != null) loader.onContentChanged(); + } +} diff --git a/app/src/main/java/com/fastaccess/provider/receiver/BootReceiver.java b/app/src/main/java/com/fastaccess/provider/receiver/BootReceiver.java new file mode 100644 index 0000000..8e46132 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/receiver/BootReceiver.java @@ -0,0 +1,20 @@ +package com.fastaccess.provider.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import com.fastaccess.helper.PermissionsHelper; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.provider.service.FloatingService; + +public class BootReceiver extends BroadcastReceiver { + @Override public void onReceive(Context context, Intent intent) { + if (PrefConstant.isAutoStart() && PermissionsHelper.isSystemAlertGranted(context)) { + if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { + Intent serviceIntent = new Intent(context, FloatingService.class); + context.startService(serviceIntent); + } + } + } +} diff --git a/app/src/main/java/com/fastaccess/provider/service/FloatingService.java b/app/src/main/java/com/fastaccess/provider/service/FloatingService.java new file mode 100644 index 0000000..a046f7a --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/service/FloatingService.java @@ -0,0 +1,71 @@ +package com.fastaccess.provider.service; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.IBinder; +import android.support.annotation.Nullable; + +import com.fastaccess.R; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.helper.NotificationHelper; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.ui.modules.floating.apps.FloatingVHView; +import com.fastaccess.ui.modules.floating.folders.FloatingFoldersView; +import com.fastaccess.ui.modules.main.MainView; + +/** + * Created by Kosh on 13 Oct 2016, 7:32 PM + */ + +public class FloatingService extends Service { + private FloatingVHView floatingVHView; + private FloatingFoldersView floatingFoldersView; + + @Override public void onCreate() { + super.onCreate(); + String floatingMode = PrefHelper.getString(PrefConstant.FLOATING_MODE); + if (!InputHelper.isEmpty(floatingMode)) { + switch (floatingMode) { + case "Apps": + floatingVHView = FloatingVHView.with(this, PrefConstant.isHorizontal()); + break; + case "Folders": + floatingFoldersView = FloatingFoldersView.with(this, PrefConstant.isHorizontal()); + break; + } + } else { + floatingVHView = FloatingVHView.with(this, PrefConstant.isHorizontal()); + } + startForeground(NotificationHelper.NOTIFICATION_ID, NotificationHelper.getNonCancellableNotification(this, + getString(R.string.app_name), getString(R.string.click_to_open_fa), + PrefHelper.getBoolean(PrefConstant.STATUS_BAR_HIDDEN) ? R.drawable.ic_notification : R.drawable.ic_fa_notification, + PendingIntent.getActivity(this, 0, new Intent(this, MainView.class), PendingIntent.FLAG_UPDATE_CURRENT))); + } + + @Nullable @Override public IBinder onBind(Intent intent) { + return null; + } + + @Override public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + return START_STICKY; + } + + @Override public void onTaskRemoved(Intent rootIntent) { + PendingIntent service = PendingIntent.getService(getApplicationContext(), 1001, + new Intent(getApplicationContext(), FloatingService.class), PendingIntent.FLAG_ONE_SHOT); + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 3000, service); + super.onTaskRemoved(rootIntent); + } + + @Override public void onDestroy() { + super.onDestroy(); + if (floatingVHView != null) floatingVHView.onDestroy(); + if (floatingFoldersView != null) floatingFoldersView.onDestroy(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/DeviceAppsAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/DeviceAppsAdapter.java new file mode 100644 index 0000000..3844896 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/DeviceAppsAdapter.java @@ -0,0 +1,97 @@ +package com.fastaccess.ui.adapter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.ViewGroup; +import android.widget.Filter; +import android.widget.Filterable; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.ui.adapter.viewholder.DeviceAppsViewHolder; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class DeviceAppsAdapter extends BaseRecyclerAdapter> implements Filterable { + private Map selection; + + public DeviceAppsAdapter(@NonNull List data, @Nullable DeviceAppsViewHolder.OnItemClickListener listener, + Map selection) { + super(data, listener); + this.selection = selection; + } + + @Override protected DeviceAppsViewHolder viewHolder(ViewGroup parent, int viewType) { + return new DeviceAppsViewHolder(BaseViewHolder.getView(parent, R.layout.app_row_item), this); + } + + @Override protected void onBindView(DeviceAppsViewHolder holder, int position) { + AppsModel model = getItem(position); + if (model != null) { + holder.bind(model, isSelected(model.getComponentName().toShortString())); + } + } + + public void select(String packageName, int position, boolean select) { + if (select) selection.put(packageName, getItem(position)); + else selection.remove(packageName); + notifyItemChanged(position); + } + + public boolean isSelected(@NonNull String packageName) { + return selection.get(packageName) != null; + } + + public boolean hasSelection() { + return selection != null && !selection.isEmpty(); + } + + public void clearSelection() { + if (hasSelection()) selection.clear(); + notifyDataSetChanged(); + } + + public int selectionSize() { + return selection != null ? selection.size() : 0; + } + + public List getSelections() { + return new ArrayList<>(selection.values()); + } + + @Override public Filter getFilter() { + return new Filter() { + @Override protected FilterResults performFiltering(CharSequence charSequence) { + final FilterResults oReturn = new FilterResults(); + final List results = new ArrayList<>(); + if (!InputHelper.isEmpty(charSequence)) { + if (!getData().isEmpty()) { + for (AppsModel appInfo : getData()) { + if (appInfo.getAppName().toLowerCase().contains(charSequence.toString())) { + results.add(appInfo); + } + } + } + oReturn.values = results; + oReturn.count = results.size(); + } + return oReturn; + } + + @Override protected void publishResults(CharSequence constraint, FilterResults results) { + insertItems((List) results.values); + } + }; + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/FloatingAppsAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/FloatingAppsAdapter.java new file mode 100644 index 0000000..a05b123 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/FloatingAppsAdapter.java @@ -0,0 +1,39 @@ +package com.fastaccess.ui.adapter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.ViewGroup; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.adapter.viewholder.FloatingAppsViewHolder; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; + +import java.util.List; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class FloatingAppsAdapter extends BaseRecyclerAdapter> { + + private boolean isHorizontal; + + public FloatingAppsAdapter(@NonNull List data, + @Nullable FloatingAppsViewHolder.OnItemClickListener listener, boolean isHorizontal) { + super(data, listener); + this.isHorizontal = isHorizontal; + } + + @Override protected FloatingAppsViewHolder viewHolder(ViewGroup parent, int viewType) { + return FloatingAppsViewHolder.newInstance(parent, this); + } + + @Override protected void onBindView(FloatingAppsViewHolder holder, int position) { + AppsModel model = getItem(position); + if (model != null) { + holder.bind(model, isHorizontal); + } + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/FloatingFoldersAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/FloatingFoldersAdapter.java new file mode 100644 index 0000000..17eefa3 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/FloatingFoldersAdapter.java @@ -0,0 +1,39 @@ +package com.fastaccess.ui.adapter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.ViewGroup; + +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.ui.adapter.viewholder.FloatingFoldersViewHolder; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; + +import java.util.List; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class FloatingFoldersAdapter extends BaseRecyclerAdapter> { + + private boolean isHorizontal; + + public FloatingFoldersAdapter(@NonNull List data, @Nullable FloatingFoldersViewHolder.OnItemClickListener listener, + boolean isHorizontal) { + super(data, listener); + this.isHorizontal = isHorizontal; + } + + @Override protected FloatingFoldersViewHolder viewHolder(ViewGroup parent, int viewType) { + return FloatingFoldersViewHolder.newInstance(parent, this); + } + + @Override protected void onBindView(FloatingFoldersViewHolder holder, int position) { + FolderModel model = getItem(position); + if (model != null) { + holder.bind(model, isHorizontal); + } + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/FoldersAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/FoldersAdapter.java new file mode 100644 index 0000000..278393d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/FoldersAdapter.java @@ -0,0 +1,59 @@ +package com.fastaccess.ui.adapter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.ViewGroup; +import android.widget.Filter; +import android.widget.Filterable; + +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.ui.adapter.viewholder.FoldersViewHolder; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Kosh on 11 Oct 2016, 7:58 PM + */ + +public class FoldersAdapter extends BaseRecyclerAdapter> implements Filterable { + public FoldersAdapter(@NonNull List data, @Nullable FoldersViewHolder.OnItemClickListener listener) { + super(data, listener); + } + + @Override protected FoldersViewHolder viewHolder(ViewGroup parent, int viewType) { + return FoldersViewHolder.newInstance(parent, this); + } + + @Override protected void onBindView(FoldersViewHolder holder, int position) { + holder.bind(getItem(position)); + } + + @Override public Filter getFilter() { + return new Filter() { + @Override protected FilterResults performFiltering(CharSequence charSequence) { + final FilterResults oReturn = new FilterResults(); + final List results = new ArrayList<>(); + if (!InputHelper.isEmpty(charSequence)) { + if (!getData().isEmpty()) { + for (FolderModel folder : getData()) { + if (folder.getFolderName().toLowerCase().contains(charSequence.toString())) { + results.add(folder); + } + } + } + oReturn.values = results; + oReturn.count = results.size(); + } + return oReturn; + } + + @Override protected void publishResults(CharSequence constraint, FilterResults results) { + insertItems((List) results.values); + } + }; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/SelectFolderAppsAdapter.java b/app/src/main/java/com/fastaccess/ui/adapter/SelectFolderAppsAdapter.java new file mode 100644 index 0000000..d5f666d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/SelectFolderAppsAdapter.java @@ -0,0 +1,64 @@ +package com.fastaccess.ui.adapter; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.ViewGroup; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.adapter.viewholder.SelectFolderAppsViewHolder; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class SelectFolderAppsAdapter extends BaseRecyclerAdapter> { + private Map selection; + + public SelectFolderAppsAdapter(@NonNull List data, @Nullable SelectFolderAppsViewHolder.OnItemClickListener listener, + Map selection) { + super(data, listener); + this.selection = selection; + } + + @Override protected SelectFolderAppsViewHolder viewHolder(ViewGroup parent, int viewType) { + return SelectFolderAppsViewHolder.newInstance(parent, this); + } + + @Override protected void onBindView(SelectFolderAppsViewHolder holder, int position) { + AppsModel model = getItem(position); + holder.bind(model, isSelected(model.getActivityInfoName())); + } + + public void select(String packageName, int position, boolean select) { + if (select) selection.put(packageName, getItem(position)); + else selection.remove(packageName); + notifyItemChanged(position); + } + + public boolean isSelected(@NonNull String packageName) { + return selection.get(packageName) != null; + } + + public boolean hasSelection() { + return selection != null && !selection.isEmpty(); + } + + public void clearSelection() { + if (hasSelection()) selection.clear(); + notifyDataSetChanged(); + } + + public int selectionSize() { + return selection != null ? selection.size() : 0; + } + + public List getSelections() { + return new ArrayList<>(selection.values()); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/AppDrawerHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/AppDrawerHolder.java new file mode 100644 index 0000000..ce621b7 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/AppDrawerHolder.java @@ -0,0 +1,43 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.view.MotionEvent; +import android.view.View; + +import com.fastaccess.R; +import com.fastaccess.ui.modules.floating.folders.drawer.FloatingDrawerMvp; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.floating.FloatingLayout; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnTouch; +import butterknife.Unbinder; + +public class AppDrawerHolder { + @BindView(R.id.appDrawer) public FloatingLayout appDrawer; + @BindView(R.id.recycler) public DynamicRecyclerView recycler; + @BindView(R.id.empty_text) public FontTextView emptyText; + private FloatingDrawerMvp.View viewCallback; + private Unbinder unbinder; + + @OnTouch(R.id.appDrawer) boolean onTouch(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { + if (viewCallback != null) viewCallback.onTouchedOutside(); + } + return false; + } + + public AppDrawerHolder(View view, FloatingDrawerMvp.View viewCallback) { + this.viewCallback = viewCallback; + unbinder = ButterKnife.bind(this, view); + appDrawer.setViewCallback(viewCallback); + } + + public void onDestroy() { + appDrawer.setViewCallback(null); + viewCallback = null; + if (unbinder != null) unbinder.unbind(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/DeviceAppsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/DeviceAppsViewHolder.java new file mode 100644 index 0000000..0e011c6 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/DeviceAppsViewHolder.java @@ -0,0 +1,60 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v7.widget.CardView; +import android.view.View; +import android.widget.ImageView; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.widgets.FastBitmapDrawable; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; +import com.fastaccess.ui.widgets.recyclerview.touch.ItemTouchHelperViewHolder; + +import butterknife.BindView; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class DeviceAppsViewHolder extends BaseViewHolder implements ItemTouchHelperViewHolder { + @BindView(R.id.appIcon) ImageView appIcon; + @BindView(R.id.cardView) CardView cardView; + @ColorInt private int selectedColor; + @ColorInt private int normalColor; + private boolean selected; + + public DeviceAppsViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter) { + super(itemView, adapter); + selectedColor = ActivityCompat.getColor(itemView.getContext(), R.color.light_gray); + normalColor = ActivityCompat.getColor(itemView.getContext(), R.color.cardview_light_background); + appIcon.setOnClickListener(this); + appIcon.setOnLongClickListener(this); + } + + public void bind(@NonNull AppsModel model, boolean selected) { + this.selected = selected; + bind(model); + } + + @Override public void bind(@NonNull AppsModel model) { + FastBitmapDrawable drawable = new FastBitmapDrawable(model.getBitmap()); + appIcon.setImageDrawable(drawable); + appIcon.setContentDescription(model.getAppName()); + drawable.setGhostModeEnabled(selected); + drawable.setPressed(selected); + cardView.setCardBackgroundColor(selected ? selectedColor : normalColor); + } + + @Override public void onItemSelected() { + cardView.setAlpha(0.5f); + } + + @Override public void onItemClear() { + cardView.setAlpha(1); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingAppsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingAppsViewHolder.java new file mode 100644 index 0000000..bb3ffb9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingAppsViewHolder.java @@ -0,0 +1,70 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.ui.widgets.FastBitmapDrawable; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import butterknife.BindView; +import butterknife.OnTouch; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class FloatingAppsViewHolder extends BaseViewHolder { + + @BindView(R.id.imageIcon) ImageView imageIcon; + @BindView(R.id.iconHolder) RelativeLayout iconHolder; + private boolean isHorizontal; + + @OnTouch(R.id.imageIcon) boolean onTouch(MotionEvent event) { + FastBitmapDrawable drawable = (FastBitmapDrawable) imageIcon.getDrawable(); + if (drawable != null) { + if (event.getAction() == MotionEvent.ACTION_UP || + event.getAction() == MotionEvent.ACTION_CANCEL) { + drawable.setPressed(false); + } else if (event.getAction() == MotionEvent.ACTION_DOWN) { + drawable.setPressed(true); + } + } + return false; + } + + public static FloatingAppsViewHolder newInstance(@NonNull ViewGroup parent, @NonNull BaseRecyclerAdapter adapter) { + return new FloatingAppsViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.floating_apps_row_item, parent, false), adapter); + } + + public FloatingAppsViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter) { + super(itemView, adapter); + iconHolder.setOnClickListener(null); + iconHolder.setOnLongClickListener(null); + imageIcon.setOnClickListener(this); + imageIcon.setOnLongClickListener(this); + } + + public void bind(@NonNull AppsModel model, boolean isHorizontal) { + this.isHorizontal = isHorizontal; + bind(model); + } + + @Override public void bind(@NonNull AppsModel model) { + imageIcon.setImageDrawable(new FastBitmapDrawable(model.getBitmap())); + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) imageIcon.getLayoutParams(); + int gap = PrefConstant.getGapSize(imageIcon.getResources()); + if (!isHorizontal) params.setMargins(0, 0, 0, gap); + else params.setMargins(0, 0, gap, 0); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingFoldersViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingFoldersViewHolder.java new file mode 100644 index 0000000..c444afd --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingFoldersViewHolder.java @@ -0,0 +1,63 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import com.amulyakhare.textdrawable.TextDrawable; +import com.amulyakhare.textdrawable.util.ColorGenerator; +import com.fastaccess.R; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import butterknife.BindView; + +/** + * Created by Kosh on 11 Oct 2016, 7:47 PM + */ + +public class FloatingFoldersViewHolder extends BaseViewHolder { + + @BindView(R.id.imageIcon) ImageView imageIcon; + @BindView(R.id.iconHolder) RelativeLayout iconHolder; + private boolean isHorizontal; + + public static FloatingFoldersViewHolder newInstance(@NonNull ViewGroup parent, @NonNull BaseRecyclerAdapter adapter) { + return new FloatingFoldersViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.floating_apps_row_item, parent, false), + adapter); + } + + public FloatingFoldersViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter) { + super(itemView, adapter); + iconHolder.setOnClickListener(null); + iconHolder.setOnLongClickListener(null); + imageIcon.setOnClickListener(this); + imageIcon.setOnLongClickListener(this); + } + + public void bind(@NonNull FolderModel model, boolean isHorizontal) { + this.isHorizontal = isHorizontal; + bind((model)); + } + + @Override public void bind(@NonNull FolderModel folderModel) { + TextDrawable.IBuilder builder = TextDrawable.builder() + .beginConfig() + .endConfig() + .round(); + String letter = InputHelper.getTwoLetters(folderModel.getFolderName()); + int color = folderModel.getColor() == 0 ? ColorGenerator.MATERIAL.getRandomColor() : folderModel.getColor(); + imageIcon.setImageDrawable(builder.build(letter.toUpperCase(), color)); + RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) imageIcon.getLayoutParams(); + int gap = PrefConstant.getGapSize(imageIcon.getResources()); + if (!isHorizontal) params.setMargins(0, 0, 0, gap); + else params.setMargins(0, 0, gap, 0); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingWindowsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingWindowsViewHolder.java new file mode 100644 index 0000000..a6dbb76 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FloatingWindowsViewHolder.java @@ -0,0 +1,66 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ImageView; + +import com.fastaccess.R; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.ui.modules.floating.BaseFloatingMvp; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.OnTouch; +import butterknife.Unbinder; + +/** + * Created by Kosh on 15 Oct 2016, 2:43 AM + */ + +public class FloatingWindowsViewHolder { + + private BaseFloatingMvp.BaseView callback; + private Unbinder unbinder; + + @BindView(R.id.toggleTapBar) public ImageView toggleTapBar; + @BindView(R.id.recycler) public DynamicRecyclerView recycler; + @BindView(R.id.tabBar) public View tabBar; + + @OnTouch(R.id.tabBar) boolean onTouch(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { + if (callback != null) callback.onTouchedOutside(); + } + return false; + } + + @OnClick(R.id.toggleTapBar) void onToggle() { + if (callback != null) callback.onToggleVisibility(true); + } + + public FloatingWindowsViewHolder(@NonNull View view, @NonNull BaseFloatingMvp.BaseView callback) { + this.callback = callback; + unbinder = ButterKnife.bind(this, view); + onSetupBackground(); + } + + public void onSetupBackground() { + int bgColor = PrefHelper.getInt(PrefConstant.FA_BACKGROUND); + int bgAlpha = PrefHelper.getInt(PrefConstant.FA_BACKGROUND_ALPHA); + Drawable drawable = new ColorDrawable(bgColor == 0 ? Color.TRANSPARENT : bgColor); + drawable.setAlpha(bgAlpha == 0 ? 255 : bgAlpha); + tabBar.setBackground(drawable); + } + + public void onDestroy() { + callback = null; + callback = null; + if (unbinder != null) unbinder.unbind(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FoldersViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FoldersViewHolder.java new file mode 100644 index 0000000..db7e01c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/FoldersViewHolder.java @@ -0,0 +1,62 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.amulyakhare.textdrawable.TextDrawable; +import com.amulyakhare.textdrawable.util.ColorGenerator; +import com.fastaccess.R; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import butterknife.BindView; + +/** + * Created by Kosh on 11 Oct 2016, 7:47 PM + */ + +public class FoldersViewHolder extends BaseViewHolder { + + @BindView(R.id.folderImage) ImageView folderImage; + @BindView(R.id.folderName) FontTextView folderName; + @BindView(R.id.appsCount) FontTextView appsCount; + @BindView(R.id.addApps) View addApps; + @BindView(R.id.delete) View delete; + @BindView(R.id.editFolder) View editFolder; + + public static FoldersViewHolder newInstance(@NonNull ViewGroup parent, @NonNull BaseRecyclerAdapter adapter) { + return new FoldersViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.folder_row_item, parent, false), adapter); + } + + public FoldersViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter) { + super(itemView, adapter); + folderImage.setOnClickListener(this); + folderImage.setOnLongClickListener(this); + addApps.setOnClickListener(this); + addApps.setOnLongClickListener(this); + editFolder.setOnClickListener(this); + editFolder.setOnLongClickListener(this); + delete.setOnClickListener(this); + delete.setOnLongClickListener(this); + } + + @Override public void bind(@NonNull FolderModel folderModel) { + folderName.setText(folderModel.getFolderName()); + appsCount.setText(String.valueOf(folderModel.getAppsCount())); + folderImage.setContentDescription(folderModel.getFolderName()); + TextDrawable.IBuilder builder = TextDrawable.builder() + .beginConfig() + .endConfig() + .round(); + String letter = InputHelper.getTwoLetters(folderModel.getFolderName()); + int color = folderModel.getColor() == 0 ? ColorGenerator.MATERIAL.getRandomColor() : folderModel.getColor(); + folderImage.setImageDrawable(builder.build(letter.toUpperCase(), color)); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/adapter/viewholder/SelectFolderAppsViewHolder.java b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/SelectFolderAppsViewHolder.java new file mode 100644 index 0000000..5668f83 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/adapter/viewholder/SelectFolderAppsViewHolder.java @@ -0,0 +1,49 @@ +package com.fastaccess.ui.adapter.viewholder; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.widgets.FontCheckbox; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import butterknife.BindView; + +/** + * Created by Kosh on 30 Aug 2016, 11:42 PM + */ + +public class SelectFolderAppsViewHolder extends BaseViewHolder { + @BindView(R.id.appIcon) ImageView appIcon; + @BindView(R.id.appName) FontTextView appName; + @BindView(R.id.checkbox) FontCheckbox checkbox; + private boolean selected; + + public static SelectFolderAppsViewHolder newInstance(@NonNull ViewGroup parent, @NonNull BaseRecyclerAdapter adapter) { + return new SelectFolderAppsViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.select_apps_folder_row, parent, false), adapter); + } + + public SelectFolderAppsViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter) { + super(itemView, adapter); + } + + public void bind(@NonNull AppsModel model, boolean selected) { + this.selected = selected; + bind(model); + } + + @Override public void bind(@NonNull AppsModel model) { + appName.setText(model.getAppName()); + appIcon.setImageBitmap(model.getBitmap()); + appIcon.setContentDescription(model.getAppName()); + checkbox.setChecked(selected); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java new file mode 100644 index 0000000..d519354 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java @@ -0,0 +1,126 @@ +package com.fastaccess.ui.base; + +import android.os.Bundle; +import android.support.annotation.DrawableRes; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.view.View; + +import com.fastaccess.BuildConfig; +import com.fastaccess.R; +import com.fastaccess.helper.AppHelper; +import com.fastaccess.helper.ViewHelper; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; +import com.fastaccess.ui.widgets.dialog.MessageDialogView; + +import butterknife.BindView; +import butterknife.ButterKnife; +import icepick.Icepick; + +/** + * Created by Kosh on 24 May 2016, 8:48 PM + */ + +public abstract class BaseActivity> extends AppCompatActivity implements + MessageDialogView.MessageDialogViewActionCallback { + + @LayoutRes protected abstract int layout(); + + @Nullable @BindView(R.id.toolbar) Toolbar toolbar; + + @NonNull protected abstract P getPresenter(); + + protected abstract boolean isTransparent(); + + protected abstract boolean canBack(); + + @Override protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Icepick.saveInstanceState(this, outState); + } + + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (layout() != 0) { + setContentView(layout()); + ButterKnife.bind(this); + } + Icepick.setDebug(BuildConfig.DEBUG); + if (savedInstanceState != null && !savedInstanceState.isEmpty()) { + Icepick.restoreInstanceState(this, savedInstanceState); + } + setupToolbarAndStatusBar(); +// if (PermissionHelper.declinedPermissions(this.getApplicationContext(), PermissionActivity.PERMISSIONS).length != 0) { +// startActivity(new Intent(this, PermissionActivity.class)); +// finish(); +// return; +// } + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { + if (canBack()) { + if (item.getItemId() == android.R.id.home) { + supportFinishAfterTransition(); + } + } + return super.onOptionsItemSelected(item); + } + + @Override protected void onDestroy() { + //noinspection ConstantConditions + if (getPresenter() != null) getPresenter().onDestroy(); + super.onDestroy(); + } + + @Override public void onDialogDismissed() { + + }//pass + + @Override public void onMessageDialogActionClicked(boolean isOk, int requestCode) { + + }//pass + + private void setupToolbarAndStatusBar() { + if (AppHelper.isLollipopOrHigher()) { + changeAppColor(); + } + if (toolbar != null) { + setSupportActionBar(toolbar); + if (canBack()) { + if (getSupportActionBar() != null) { + if (toolbar != null) { + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + supportFinishAfterTransition(); + } + }); + } + getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_back); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + } + } + } + + protected void setToolbarIcon(@DrawableRes int res) { + if (getSupportActionBar() != null) { + getSupportActionBar().setHomeAsUpIndicator(res); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + } + + private void changeAppColor() { + if (AppHelper.isLollipopOrHigher()) { + if (!isTransparent()) { + getWindow().setStatusBarColor(ViewHelper.getPrimaryDarkColor(this)); + } else { + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + } + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseBottomSheetDialog.java b/app/src/main/java/com/fastaccess/ui/base/BaseBottomSheetDialog.java new file mode 100644 index 0000000..456a338 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/base/BaseBottomSheetDialog.java @@ -0,0 +1,123 @@ +package com.fastaccess.ui.base; + +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.BottomSheetBehavior; +import android.support.design.widget.BottomSheetDialogFragment; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; + +import com.fastaccess.helper.Logger; +import com.fastaccess.helper.ViewHelper; + +import butterknife.ButterKnife; +import butterknife.Unbinder; +import icepick.Icepick; + +/** + * Created by Kosh on 16 Sep 2016, 2:11 PM + */ + +public abstract class BaseBottomSheetDialog extends BottomSheetDialogFragment { + + protected BottomSheetBehavior bottomSheetBehavior; + protected boolean isAlreadyHidden; + @Nullable private Unbinder unbinder; + + @LayoutRes protected abstract int layoutRes(); + + protected abstract void onViewCreated(@NonNull View view); + + @Override public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Icepick.saveInstanceState(this, outState); + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null && !savedInstanceState.isEmpty()) { + Icepick.restoreInstanceState(this, savedInstanceState); + } + } + + @Override public void setupDialog(Dialog dialog, int style) { + super.setupDialog(dialog, style); + View contentView = View.inflate(getContext(), layoutRes(), null); + dialog.setContentView(contentView); + View parent = ((View) contentView.getParent()); + bottomSheetBehavior = BottomSheetBehavior.from(parent); + if (bottomSheetBehavior != null) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { + @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + isAlreadyHidden = true; + onHidden(); + } + } + + @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { + if (slideOffset == -1.0) { + isAlreadyHidden = true; + onDismissedByScrolling(); + } + } + }); + } + unbinder = ButterKnife.bind(this, contentView); + onViewCreated(contentView); + } + + @Override public void onDestroyView() { + super.onDestroyView(); + if (unbinder != null) unbinder.unbind(); + } + + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + final Dialog dialog = super.onCreateDialog(savedInstanceState); + if (ViewHelper.isTablet(getContext())) { + dialog.setOnShowListener(new DialogInterface.OnShowListener() { + @Override public void onShow(DialogInterface dialogINterface) { + if (dialog.getWindow() != null) dialog.getWindow().setLayout( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.MATCH_PARENT); + } + }); + } + dialog.setOnKeyListener(new DialogInterface.OnKeyListener() { + @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + isAlreadyHidden = true; + onDismissedByScrolling(); + } + return false; + } + }); + + return dialog; + } + + @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Logger.e(); + } + + @Override public void onDetach() { + if (!isAlreadyHidden) { + onDismissedByScrolling(); + } + super.onDetach(); + } + + protected void onHidden() { + dismiss(); + } + + protected void onDismissedByScrolling() {} + +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java b/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java new file mode 100644 index 0000000..05454a9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/base/BaseFragment.java @@ -0,0 +1,76 @@ +package com.fastaccess.ui.base; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +import butterknife.ButterKnife; +import butterknife.Unbinder; +import icepick.Icepick; + +/** + * Created by Kosh on 27 May 2016, 7:54 PM + */ + +public abstract class BaseFragment> extends Fragment { + + @Nullable private Unbinder unbinder; + + @LayoutRes protected abstract int fragmentLayout(); + + @NonNull protected abstract P getPresenter(); + + protected abstract void onFragmentCreated(View view, @Nullable Bundle savedInstanceState); + + @Override public void onAttach(Context context) { + super.onAttach(context); + } + + @Override public void onDetach() { + super.onDetach(); + } + + @Override public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + Icepick.saveInstanceState(this, outState); + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null && !savedInstanceState.isEmpty()) { + Icepick.restoreInstanceState(this, savedInstanceState); + } + } + + @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (fragmentLayout() != 0) { + View view = inflater.inflate(fragmentLayout(), container, false); + unbinder = ButterKnife.bind(this, view); + return view; + } + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + onFragmentCreated(view, savedInstanceState); + } + + @Override public void onDestroyView() { + getPresenter().onDestroy(); + super.onDestroyView(); + if (unbinder != null) unbinder.unbind(); + } + + protected boolean isSafe() { + return getView() != null && getActivity() != null && !getActivity().isFinishing(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/base/mvp/BaseMvp.java b/app/src/main/java/com/fastaccess/ui/base/mvp/BaseMvp.java new file mode 100644 index 0000000..348534c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/base/mvp/BaseMvp.java @@ -0,0 +1,14 @@ +package com.fastaccess.ui.base.mvp; + +/** + * Created by Kosh on 25 May 2016, 9:09 PM + */ + +public interface BaseMvp { + + interface FAPresenter { + void attachView(V view); + + void onDestroy(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/base/mvp/presenter/BasePresenter.java b/app/src/main/java/com/fastaccess/ui/base/mvp/presenter/BasePresenter.java new file mode 100644 index 0000000..6b331b8 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/base/mvp/presenter/BasePresenter.java @@ -0,0 +1,42 @@ +package com.fastaccess.ui.base.mvp.presenter; + +import android.support.annotation.NonNull; + +import com.fastaccess.ui.base.mvp.BaseMvp; + +/** + * Created by Kosh on 25 May 2016, 9:12 PM + */ + +public class BasePresenter implements BaseMvp.FAPresenter { + + private V view; + + private BasePresenter() {throw new RuntimeException("Cant not be initialized");} + + protected BasePresenter(@NonNull V view) { + attachView(view); + } + + @Override public void attachView(@NonNull V view) { + this.view = view; + } + + @Override public void onDestroy() { + view = null; + } + + protected boolean isAttached() { + return view != null; + } + + protected V getView() { + checkViewAttached(); + return view; + } + + private void checkViewAttached() { + if (!isAttached()) throw new NullPointerException("View is not injected to presenter"); + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsMvp.java b/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsMvp.java new file mode 100644 index 0000000..fb3740a --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsMvp.java @@ -0,0 +1,46 @@ +package com.fastaccess.ui.modules.apps.device; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.LoaderManager; +import android.support.v7.view.ActionMode; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.List; + +/** + * Created by Kosh on 10 Oct 2016, 11:40 PM + */ + +public interface DeviceAppsMvp { + + interface View { + void onStartLoading(); + + void onAppsLoaded(@Nullable List data); + + void onLoaderReset(); + + void setSelection(@NonNull String componentName, int position); + + boolean hasSelection(); + + void onActionModeDestroyed(); + + void onOpenAppDetails(@NonNull android.view.View view, @NonNull AppsModel appsModel); + + void onAddSelectedApps(); + + void onFilter(@Nullable String text); + + void onSelectAll(); + } + + interface Presenter extends LoaderManager.LoaderCallbacks>, + BaseViewHolder.OnItemClickListener, + ActionMode.Callback { + void onAddSelectedApps(@Nullable List selections); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsPresenter.java new file mode 100644 index 0000000..aa2e0ca --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsPresenter.java @@ -0,0 +1,104 @@ +package com.fastaccess.ui.modules.apps.device; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.support.v7.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import com.fastaccess.App; +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.data.dao.FloatingEventModel; +import com.fastaccess.data.dao.SelectedAppsEventModel; +import com.fastaccess.helper.Logger; +import com.fastaccess.provider.loader.DeviceAppsLoader; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +import org.greenrobot.eventbus.EventBus; + +import java.util.List; + +/** + * Created by Kosh on 10 Oct 2016, 11:45 PM + */ + +public class DeviceAppsPresenter extends BasePresenter implements DeviceAppsMvp.Presenter { + + protected DeviceAppsPresenter(@NonNull DeviceAppsMvp.View view) { + super(view); + } + + public static DeviceAppsPresenter with(@NonNull DeviceAppsMvp.View view) { + return new DeviceAppsPresenter(view); + } + + @Override public Loader> onCreateLoader(int id, Bundle args) { + if (isAttached()) getView().onStartLoading(); + return new DeviceAppsLoader(App.getInstance().getApplicationContext()); + } + + @Override public void onLoadFinished(Loader> loader, List data) { + getView().onAppsLoaded(data); + } + + @Override public void onLoaderReset(Loader> loader) { + if (isAttached()) getView().onLoaderReset(); + } + + @Override public void onItemClick(int position, View v, AppsModel item) { + if (getView().hasSelection()) { + onItemLongClick(position, v, item); + } else { + getView().onOpenAppDetails(v, item); + } + } + + @Override public void onItemLongClick(int position, View v, AppsModel item) { + getView().setSelection(item.getComponentName().toShortString(), position); + } + + @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.getMenuInflater().inflate(R.menu.add_menu, menu); + return true; + } + + @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getItemId() == R.id.add) { + getView().onAddSelectedApps(); + return true; + } else if (item.getItemId() == R.id.selectAll) { + getView().onSelectAll(); + return true; + } + return false; + } + + @Override public void onDestroyActionMode(ActionMode mode) { + getView().onActionModeDestroyed(); + } + + @Override public void onAddSelectedApps(@Nullable List selections) { + if (selections != null && !selections.isEmpty()) { + for (int i = 0; i < selections.size(); i++) { + AppsModel model = selections.get(i); + if (AppsModel.exists(model.getActivityInfoName(), model.getPackageName())) { + continue; + } + int lastPosition = AppsModel.lastPosition() + 1; + Logger.e(lastPosition); + model.setIndexPosition(lastPosition); + model.save(); + } + EventBus.getDefault().post(new SelectedAppsEventModel()); + EventBus.getDefault().post(new FloatingEventModel()); + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsView.java b/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsView.java new file mode 100644 index 0000000..30829a9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/device/DeviceAppsView.java @@ -0,0 +1,167 @@ +package com.fastaccess.ui.modules.apps.device; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.support.v4.widget.NestedScrollView; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.view.ActionMode; +import android.view.View; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.data.dao.DeviceAppsEventModel; +import com.fastaccess.data.dao.ThemePackEventModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.ui.adapter.DeviceAppsAdapter; +import com.fastaccess.ui.base.BaseFragment; +import com.fastaccess.ui.modules.main.MainMvp; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; +import com.mikhaellopez.circularfillableloaders.CircularFillableLoaders; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +import butterknife.BindView; +import icepick.State; + +/** + * Created by Kosh on 10 Oct 2016, 11:47 PM + */ + +public class DeviceAppsView extends BaseFragment implements DeviceAppsMvp.View { + public static final String TAG = "DeviceAppsView"; + @BindView(R.id.recycler) DynamicRecyclerView recycler; + @BindView(R.id.empty_text) FontTextView emptyText; + @BindView(R.id.empty) NestedScrollView empty; + @BindView(R.id.progressBar) CircularFillableLoaders progressBar; + @State HashMap selection = new LinkedHashMap<>(); + private DeviceAppsPresenter presenter; + private DeviceAppsAdapter adapter; + private ActionMode actionMode; + private Loader loader; + private MainMvp.View mainCallback; + + public static DeviceAppsView newInstance() { + return new DeviceAppsView(); + } + + @Override public void onAttach(Context context) { + super.onAttach(context); + if (!(context instanceof MainMvp.View)) { + throw new RuntimeException(context.getClass().getSimpleName() + " is not implementing MainMvp.View"); + } + mainCallback = (MainMvp.View) context; + EventBus.getDefault().register(this); + } + + @Override public void onDetach() { + super.onDetach(); + EventBus.getDefault().unregister(this); + mainCallback = null; + } + + @Override protected int fragmentLayout() { + return R.layout.grid_list; + } + + @NonNull @Override protected DeviceAppsPresenter getPresenter() { + if (presenter == null) { + presenter = DeviceAppsPresenter.with(this); + } + return presenter; + } + + @Override protected void onFragmentCreated(View view, @Nullable Bundle savedInstanceState) { + recycler.setEmptyView(empty); + adapter = new DeviceAppsAdapter(new ArrayList(), getPresenter(), selection); + recycler.setAdapter(adapter); + loader = getLoaderManager().initLoader(0, null, getPresenter()); + if (!selection.isEmpty()) { + actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(getPresenter()); + actionMode.setTitle(getString(R.string.selected) + " ( " + adapter.selectionSize() + " )"); + } + } + + @Override public void onStartLoading() { + recycler.showProgress(progressBar); + } + + @Override public void onAppsLoaded(@Nullable List data) { + recycler.hideProgress(progressBar); + if (data != null) adapter.insertItems(data); + else adapter.clear(); + } + + @Override public void onLoaderReset() { + recycler.hideProgress(progressBar); + adapter.clear(); + } + + @Override public void setSelection(@NonNull String packageName, int position) { + adapter.select(packageName, position, !adapter.isSelected(packageName)); + if (hasSelection()) { + if (actionMode == null) { + actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(getPresenter()); + } + actionMode.setTitle(getString(R.string.selected) + " ( " + adapter.selectionSize() + " )"); + } else { + actionMode.finish(); + } + } + + @Override public boolean hasSelection() { + return adapter.hasSelection(); + } + + @Override public void onActionModeDestroyed() { + adapter.clearSelection(); + actionMode = null; + } + + @Override public void onOpenAppDetails(@NonNull View view, @NonNull AppsModel appsModel) { + + } + + @Override public void onAddSelectedApps() { + getPresenter().onAddSelectedApps(adapter.getSelections()); + if (actionMode != null) actionMode.finish(); + if (mainCallback != null) mainCallback.onShowBadge(R.id.selectedApps); + } + + @Override public void onFilter(@Nullable String text) { + if (progressBar.isShown()) { + return; + } + if (InputHelper.isEmpty(text)) { + loader.onContentChanged(); + } else { + adapter.getFilter().filter(text); + } + } + + @Override public void onSelectAll() { + adapter.clearSelection(); + for (int i = 0; i < adapter.getData().size(); i++) { + AppsModel model = adapter.getItem(i); + setSelection(model.getComponentName().toShortString(), i); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(DeviceAppsEventModel model) { + if (loader != null) loader.onContentChanged(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(ThemePackEventModel model) { + if (loader != null) loader.forceLoad(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersMvp.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersMvp.java new file mode 100644 index 0000000..c3ed652 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersMvp.java @@ -0,0 +1,41 @@ +package com.fastaccess.ui.modules.apps.folders; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.LoaderManager; + +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.ui.modules.apps.folders.create.CreateFolderMvp; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.List; + +/** + * Created by Kosh on 11 Oct 2016, 7:32 PM + */ + +public interface FoldersMvp { + + interface View extends CreateFolderMvp.OnNotifyFoldersAdapter { + void onStartLoading(); + + void onFoldersLoaded(@Nullable List models); + + void onLoaderReset(); + + void onEditFolder(@NonNull FolderModel folder); + + void onAddAppsToFolder(@NonNull FolderModel folder); + + void onCreateNewFolder(); + + void onDeleteFolder(@NonNull FolderModel folder, int position); + + void onFilter(@Nullable String text); + } + + interface Presenter extends LoaderManager.LoaderCallbacks>, + BaseViewHolder.OnItemClickListener { + + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersPresenter.java new file mode 100644 index 0000000..fc3d1d7 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersPresenter.java @@ -0,0 +1,55 @@ +package com.fastaccess.ui.modules.apps.folders; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.content.Loader; +import android.view.View; + +import com.fastaccess.App; +import com.fastaccess.R; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.provider.loader.FoldersLoader; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +import java.util.List; + +/** + * Created by Kosh on 11 Oct 2016, 7:34 PM + */ + +public class FoldersPresenter extends BasePresenter implements FoldersMvp.Presenter { + protected FoldersPresenter(@NonNull FoldersMvp.View view) { + super(view); + } + + public static FoldersPresenter with(@NonNull FoldersMvp.View view) { + return new FoldersPresenter(view); + } + + @Override public Loader> onCreateLoader(int id, Bundle args) { + if (isAttached()) getView().onStartLoading(); + return new FoldersLoader(App.getInstance().getApplicationContext()); + } + + @Override public void onLoadFinished(Loader> loader, List data) { + getView().onFoldersLoaded(data); + } + + @Override public void onLoaderReset(Loader> loader) { + if (isAttached()) getView().onLoaderReset(); + } + + @Override public void onItemClick(int position, View v, FolderModel item) { + if (v.getId() == R.id.folderImage || v.getId() == R.id.editFolder) { + getView().onEditFolder(item); + } else if (v.getId() == R.id.delete) { + getView().onDeleteFolder(item, position); + } else { + getView().onAddAppsToFolder(item); + } + } + + @Override public void onItemLongClick(int position, View v, FolderModel item) { + onItemClick(position, v, item); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersView.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersView.java new file mode 100644 index 0000000..f51190f --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/FoldersView.java @@ -0,0 +1,115 @@ +package com.fastaccess.ui.modules.apps.folders; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.support.v4.widget.NestedScrollView; +import android.view.View; + +import com.fastaccess.R; +import com.fastaccess.data.dao.FolderEventModel; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.ui.adapter.FoldersAdapter; +import com.fastaccess.ui.base.BaseFragment; +import com.fastaccess.ui.modules.apps.folders.create.CreateFolderView; +import com.fastaccess.ui.modules.apps.folders.select.SelectFolderAppsView; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; +import com.mikhaellopez.circularfillableloaders.CircularFillableLoaders; + +import org.greenrobot.eventbus.EventBus; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; + +/** + * Created by Kosh on 11 Oct 2016, 7:42 PM + */ + +public class FoldersView extends BaseFragment implements FoldersMvp.View { + public final static String TAG = "FoldersView"; + + @BindView(R.id.recycler) DynamicRecyclerView recycler; + @BindView(R.id.empty_text) FontTextView emptyText; + @BindView(R.id.empty) NestedScrollView empty; + @BindView(R.id.progressBar) CircularFillableLoaders progressBar; + private FoldersPresenter presenter; + private FoldersAdapter adapter; + private Loader loader; + + public static FoldersView newInstance() { + return new FoldersView(); + } + + @Override protected int fragmentLayout() { + return R.layout.small_grid_list; + } + + @NonNull @Override protected FoldersPresenter getPresenter() { + if (presenter == null) { + presenter = FoldersPresenter.with(this); + } + return presenter; + } + + @Override protected void onFragmentCreated(View view, @Nullable Bundle savedInstanceState) { + adapter = new FoldersAdapter(new ArrayList(), getPresenter()); + recycler.setEmptyView(empty); + emptyText.setText(R.string.no_folders); + recycler.setAdapter(adapter); + loader = getLoaderManager().initLoader(1, null, getPresenter()); + } + + @Override public void onStartLoading() { + recycler.showProgress(progressBar); + } + + @Override public void onFoldersLoaded(@Nullable List models) { + recycler.hideProgress(progressBar); + if (models == null) adapter.clear(); + else adapter.insertItems(models); + } + + @Override public void onLoaderReset() { + recycler.hideProgress(progressBar); + adapter.clear(); + } + + @Override public void onEditFolder(@NonNull FolderModel folder) { + CreateFolderView.newInstance(folder.getId()).show(getChildFragmentManager(), "CreateFolderView"); + } + + @Override public void onAddAppsToFolder(@NonNull FolderModel folder) { + SelectFolderAppsView.newInstance(folder.getId()).show(getChildFragmentManager(), "SelectFolderAppsView"); + } + + @Override public void onCreateNewFolder() { + CreateFolderView.newInstance(-1).show(getChildFragmentManager(), "CreateFolderView"); + } + + @Override public void onDeleteFolder(@NonNull FolderModel folder, int position) { + FolderModel.deleteFolder(folder); + adapter.removeItem(position); + onNotifyChanges(); + } + + @Override public void onFilter(@Nullable String text) { + if (progressBar.isShown()) { + return; + } + if (InputHelper.isEmpty(text)) { + loader.onContentChanged(); + } else { + adapter.getFilter().filter(text); + } + } + + @Override public void onNotifyChanges() { + if (loader != null) loader.onContentChanged(); + EventBus.getDefault().post(new FolderEventModel()); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderMvp.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderMvp.java new file mode 100644 index 0000000..7495a74 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderMvp.java @@ -0,0 +1,18 @@ +package com.fastaccess.ui.modules.apps.folders.create; + +import org.xdty.preference.colorpicker.ColorPickerSwatch; + +/** + * Created by Kosh on 11 Oct 2016, 8:26 PM + */ + +public interface CreateFolderMvp { + + interface View extends ColorPickerSwatch.OnColorSelectedListener {}//op-out + + interface Presenter {}//op-out + + interface OnNotifyFoldersAdapter { + void onNotifyChanges(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderPresenter.java new file mode 100644 index 0000000..4c20d8c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderPresenter.java @@ -0,0 +1,20 @@ +package com.fastaccess.ui.modules.apps.folders.create; + +import android.support.annotation.NonNull; + +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +/** + * Created by Kosh on 11 Oct 2016, 8:26 PM + */ + +public class CreateFolderPresenter extends BasePresenter implements CreateFolderMvp.Presenter { + + protected CreateFolderPresenter(@NonNull CreateFolderMvp.View view) { + super(view); + } + + public static CreateFolderPresenter with(@NonNull CreateFolderMvp.View view) { + return new CreateFolderPresenter(view); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderView.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderView.java new file mode 100644 index 0000000..eee5f65 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/create/CreateFolderView.java @@ -0,0 +1,159 @@ +package com.fastaccess.ui.modules.apps.folders.create; + +import android.content.Context; +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.TextInputLayout; +import android.support.v4.content.ContextCompat; +import android.text.Editable; +import android.view.Gravity; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; + +import com.amulyakhare.textdrawable.TextDrawable; +import com.fastaccess.R; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.helper.Bundler; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.helper.ViewHelper; +import com.fastaccess.ui.base.BaseBottomSheetDialog; +import com.fastaccess.ui.modules.apps.folders.create.CreateFolderMvp.OnNotifyFoldersAdapter; +import com.fastaccess.ui.widgets.FontButton; + +import org.xdty.preference.colorpicker.ColorPickerDialog; + +import java.util.Date; + +import butterknife.BindView; +import butterknife.OnClick; +import butterknife.OnTextChanged; +import icepick.State; + +/** + * Created by Kosh on 11 Oct 2016, 8:27 PM + */ + +public class CreateFolderView extends BaseBottomSheetDialog implements CreateFolderMvp.View { + private long folderId; + + @State int selectedColor = Color.parseColor("#FF2A456B"); + @State String fName; + + @BindView(R.id.folderImage) ImageView folderImage; + @BindView(R.id.folderName) TextInputLayout folderName; + @BindView(R.id.cancel) FontButton cancel; + @BindView(R.id.save) FontButton save; + @BindView(R.id.folderNameEditText) EditText folderNameEditText; + private FolderModel folderModel; + private OnNotifyFoldersAdapter callback; + + private CreateFolderPresenter presenter; + + public static CreateFolderView newInstance(long folderId) { + CreateFolderView view = new CreateFolderView(); + view.setArguments(Bundler.start().put("folderId", folderId).end()); + return view; + } + + @OnClick(R.id.cancel) void onCancel() { + dismiss(); + } + + @OnClick(R.id.save) void onSave() { + boolean isEmpty = InputHelper.isEmpty(folderName); + folderName.setError(isEmpty ? getString(R.string.required_field) : null); + if (!isEmpty) { + getFolderModel().setColor(selectedColor); + getFolderModel().setFolderName(fName); + if (getFolderModel().getCreatedDate() == 0) { + getFolderModel().setCreatedDate(new Date().getTime()); + } + getFolderModel().save(); + callback.onNotifyChanges(); + dismiss(); + } + } + + @OnClick(R.id.folderImage) void onChooseColor() { + ColorPickerDialog.newInstance(R.string.color_picker_default_title, + getResources().getIntArray(R.array.colors_primary), + selectedColor != 0 ? selectedColor : ContextCompat.getColor(getContext(), R.color.primary), + (getResources().getInteger(R.integer.num_columns) + 1), ColorPickerDialog.SIZE_SMALL) + .show(getChildFragmentManager(), "ColorPickerDialog"); + } + + @OnTextChanged(value = R.id.folderNameEditText, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) void onTextChanged(Editable editable) { + boolean isEmpty = InputHelper.isEmpty(editable); + if (!isEmpty) { + setupDrawable(editable.toString()); + } + folderName.setError(isEmpty ? getString(R.string.required_field) : null); + fName = InputHelper.toString(editable); + } + + @Override public void onAttach(Context context) { + super.onAttach(context); + if (getPresenter() != null && getParentFragment() instanceof OnNotifyFoldersAdapter) { + callback = (OnNotifyFoldersAdapter) getParentFragment(); + } else if (context instanceof OnNotifyFoldersAdapter) { + callback = (OnNotifyFoldersAdapter) context; + } else { + throw new RuntimeException("Activity/Fragment must implement OnNotifyFoldersAdapter"); + } + } + + @Override public void onDetach() { + super.onDetach(); + } + + @Override protected int layoutRes() { + return R.layout.create_edit_folder; + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + folderId = getArguments().getLong("folderId", -1); + if (savedInstanceState == null) { + selectedColor = getFolderModel().getColor() != 0 ? getFolderModel().getColor() : selectedColor; + fName = getFolderModel().getFolderName(); + } + } + + @Override protected void onViewCreated(@NonNull View view) { + folderNameEditText.setText(fName); + setupDrawable(fName); + ViewHelper.showTooltip(folderImage, R.string.color_picker_hint_folder, "color_picker_folder", Gravity.TOP); + } + + @Override public void onColorSelected(int color) { + selectedColor = color; + setupDrawable(InputHelper.toString(folderNameEditText)); + } + + @NonNull private FolderModel getFolderModel() { + if (folderModel == null) { + folderModel = FolderModel.findById(FolderModel.class, folderId); + if (folderModel == null) { + folderModel = new FolderModel(); + } + } + return folderModel; + } + + private CreateFolderPresenter getPresenter() { + if (presenter == null) presenter = CreateFolderPresenter.with(this); + return presenter; + } + + private void setupDrawable(@NonNull String upDrawable) { + TextDrawable.IBuilder builder = TextDrawable.builder() + .beginConfig() + .endConfig() + .round(); + String letter = InputHelper.isEmpty(upDrawable) ? "N/A" : InputHelper.getTwoLetters(upDrawable); + folderImage.setImageDrawable(builder.build(letter.toUpperCase(), selectedColor)); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsMvp.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsMvp.java new file mode 100644 index 0000000..e29643e --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsMvp.java @@ -0,0 +1,29 @@ +package com.fastaccess.ui.modules.apps.folders.select; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.LoaderManager; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.List; + +/** + * Created by Kosh on 11 Oct 2016, 8:26 PM + */ + +public interface SelectFolderAppsMvp { + + interface View { + void onStartLoading(); + + void onAppsLoaded(@Nullable List models); + + void onLoaderReset(); + + void onRowClicked(@NonNull AppsModel model, int position); + } + + interface Presenter extends BaseViewHolder.OnItemClickListener, LoaderManager.LoaderCallbacks> {} +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsPresenter.java new file mode 100644 index 0000000..9940e8b --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsPresenter.java @@ -0,0 +1,49 @@ +package com.fastaccess.ui.modules.apps.folders.select; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.content.Loader; +import android.view.View; + +import com.fastaccess.App; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.provider.loader.DeviceAppsLoader; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +import java.util.List; + +/** + * Created by Kosh on 11 Oct 2016, 8:26 PM + */ + +public class SelectFolderAppsPresenter extends BasePresenter implements SelectFolderAppsMvp.Presenter { + + protected SelectFolderAppsPresenter(@NonNull SelectFolderAppsMvp.View view) { + super(view); + } + + public static SelectFolderAppsPresenter with(@NonNull SelectFolderAppsMvp.View view) { + return new SelectFolderAppsPresenter(view); + } + + @Override public void onItemClick(int position, View v, AppsModel item) { + getView().onRowClicked(item, position); + } + + @Override public void onItemLongClick(int position, View v, AppsModel item) { + onItemClick(position, v, item); + } + + @Override public Loader> onCreateLoader(int id, Bundle args) { + getView().onStartLoading(); + return new DeviceAppsLoader(App.getInstance().getApplicationContext()); + } + + @Override public void onLoadFinished(Loader> loader, List data) { + if (isAttached()) getView().onAppsLoaded(data); + } + + @Override public void onLoaderReset(Loader> loader) { + if (isAttached()) getView().onLoaderReset(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsView.java b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsView.java new file mode 100644 index 0000000..254611b --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/folders/select/SelectFolderAppsView.java @@ -0,0 +1,170 @@ +package com.fastaccess.ui.modules.apps.folders.select; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.AppBarLayout; +import android.support.v4.widget.NestedScrollView; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.view.View; +import android.widget.ProgressBar; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.helper.Bundler; +import com.fastaccess.helper.Logger; +import com.fastaccess.ui.adapter.SelectFolderAppsAdapter; +import com.fastaccess.ui.base.BaseBottomSheetDialog; +import com.fastaccess.ui.modules.apps.folders.create.CreateFolderMvp; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; +import com.mikhaellopez.circularfillableloaders.CircularFillableLoaders; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +import butterknife.BindView; +import icepick.State; + +/** + * Created by Kosh on 11 Oct 2016, 10:24 PM + */ + +public class SelectFolderAppsView extends BaseBottomSheetDialog implements SelectFolderAppsMvp.View { + + @State HashMap selection = new LinkedHashMap<>(); + private long folderId; + + @BindView(R.id.recycler) DynamicRecyclerView recycler; + @BindView(R.id.empty_text) FontTextView emptyText; + @BindView(R.id.empty) NestedScrollView empty; + @BindView(R.id.progressBar) CircularFillableLoaders progressBar; + @BindView(R.id.toolbar) Toolbar toolbar; + @BindView(R.id.topProgress) ProgressBar topProgress; + @BindView(R.id.appbar) AppBarLayout appbar; + private SelectFolderAppsAdapter adapter; + private SelectFolderAppsPresenter presenter; + private FolderModel folderModel; + private CreateFolderMvp.OnNotifyFoldersAdapter callback; + + public static SelectFolderAppsView newInstance(long folderId) { + SelectFolderAppsView view = new SelectFolderAppsView(); + view.setArguments(Bundler.start().put("folderId", folderId).end()); + return view; + } + + @Override public void onAttach(Context context) { + super.onAttach(context); + if (getPresenter() != null && getParentFragment() instanceof CreateFolderMvp.OnNotifyFoldersAdapter) { + callback = (CreateFolderMvp.OnNotifyFoldersAdapter) getParentFragment(); + } else if (context instanceof CreateFolderMvp.OnNotifyFoldersAdapter) { + callback = (CreateFolderMvp.OnNotifyFoldersAdapter) context; + } else { + throw new RuntimeException("Activity/Fragment must implement OnNotifyFoldersAdapter"); + } + } + + @Override public void onDetach() { + super.onDetach(); + } + + @Override protected int layoutRes() { + return R.layout.select_folder_apps_layout; + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + folderId = getArguments().getLong("folderId"); + if (savedInstanceState == null) { + List apps = AppsModel.getApps(getFolderModel().getId()); + if (!apps.isEmpty()) { + for (AppsModel m : apps) { + selection.put(m.getActivityInfoName(), m); + } + } + } + } + + @Override protected void onViewCreated(@NonNull View view) { + toolbar.setTitle(R.string.select_apps); + toolbar.setNavigationIcon(R.drawable.ic_back); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override public void onClick(View v) { + dismiss(); + } + }); + toolbar.inflateMenu(R.menu.add_menu); + toolbar.getMenu().findItem(R.id.add).setIcon(R.drawable.ic_done); + toolbar.getMenu().findItem(R.id.selectAll).setVisible(false); + toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { + @Override public boolean onMenuItemClick(MenuItem item) { + if (item.getItemId() == R.id.add) { + onAddApps(); + } + return false; + } + }); + adapter = new SelectFolderAppsAdapter(new ArrayList(), getPresenter(), selection); + recycler.setEmptyView(empty); + recycler.setAdapter(adapter); + getLoaderManager().initLoader(2, null, getPresenter()); + } + + @Override public void onStartLoading() { + recycler.showProgress(progressBar); + } + + @Override public void onAppsLoaded(@Nullable List models) { + recycler.hideProgress(progressBar); + if (models == null) { + adapter.clear(); + dismiss(); + return; + } + adapter.insertItems(models); + Logger.e(models.size()); + } + + @Override public void onLoaderReset() { + if (recycler == null) return; + recycler.hideProgress(progressBar); + adapter.clear(); + } + + @Override public void onRowClicked(@NonNull AppsModel model, int position) { + adapter.select(model.getActivityInfoName(), position, !adapter.isSelected(model.getActivityInfoName())); + } + + @NonNull private FolderModel getFolderModel() { + if (folderModel == null) { + folderModel = FolderModel.findById(FolderModel.class, folderId); + } + if (folderModel == null) { + throw new NullPointerException("folderModel is null, make sure passing the right id"); + } + return folderModel; + } + + private void onAddApps() { + List appsModels = adapter.getSelections(); + AppsModel.deleteAllByFolder(getFolderModel()); + if (appsModels != null && !appsModels.isEmpty()) { + for (AppsModel app : appsModels) { + app.setFolderId(getFolderModel().getId()); + app.save(); + } + } + callback.onNotifyChanges(); + dismiss(); + } + + public SelectFolderAppsPresenter getPresenter() { + if (presenter == null) presenter = SelectFolderAppsPresenter.with(this); + return presenter; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsMvp.java b/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsMvp.java new file mode 100644 index 0000000..796639c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsMvp.java @@ -0,0 +1,54 @@ +package com.fastaccess.ui.modules.apps.selected; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.LoaderManager; +import android.support.v7.view.ActionMode; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.adapter.DeviceAppsAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; +import com.fastaccess.ui.widgets.recyclerview.touch.ItemTouchHelperAdapter; + +import java.util.List; + +/** + * Created by Kosh on 10 Oct 2016, 11:40 PM + */ + +public interface SelectedAppsMvp { + + interface View { + void onStartLoading(); + + void onAppsLoaded(@Nullable List data); + + void onLoaderReset(); + + void setSelection(@NonNull String componentName, int position); + + boolean hasSelection(); + + void onActionModeDestroyed(); + + void onRemoveSelectedApps(); + + void onFilter(@Nullable String text); + + void onNotifyChanges(); + + void onNotifyItemMoved(int fromPosition, int toPosition); + + void onSwap(int fromPosition, int toPosition); + + void onSelectAll(); + } + + interface Presenter extends LoaderManager.LoaderCallbacks>, + BaseViewHolder.OnItemClickListener, + ActionMode.Callback, ItemTouchHelperAdapter { + void onRemoveSelectedApps(@Nullable List selections); + + void onSaveIndexChanges(@NonNull DeviceAppsAdapter adapter, int fromPosition, int toPosition); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsPresenter.java new file mode 100644 index 0000000..fbbf376 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsPresenter.java @@ -0,0 +1,118 @@ +package com.fastaccess.ui.modules.apps.selected; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.support.v7.view.ActionMode; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import com.fastaccess.App; +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.provider.loader.SelectedAppsLoader; +import com.fastaccess.ui.adapter.DeviceAppsAdapter; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +import java.util.List; + +/** + * Created by Kosh on 10 Oct 2016, 11:45 PM + */ + +public class SelectedAppsPresenter extends BasePresenter implements SelectedAppsMvp.Presenter { + + protected SelectedAppsPresenter(@NonNull SelectedAppsMvp.View view) { + super(view); + } + + public static SelectedAppsPresenter with(@NonNull SelectedAppsMvp.View view) { + return new SelectedAppsPresenter(view); + } + + @Override public Loader> onCreateLoader(int id, Bundle args) { + if (isAttached()) getView().onStartLoading(); + return new SelectedAppsLoader(App.getInstance().getApplicationContext()); + } + + @Override public void onLoadFinished(Loader> loader, List data) { + getView().onAppsLoaded(data); + } + + @Override public void onLoaderReset(Loader> loader) { + if (isAttached()) getView().onLoaderReset(); + } + + @Override public void onItemClick(int position, View v, AppsModel item) { + getView().setSelection(item.getComponentName().toShortString(), position); + } + + @Override public void onItemLongClick(int position, View v, AppsModel item) { + //op-out for drag & drop + } + + @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.getMenuInflater().inflate(R.menu.remove_menu, menu); + return true; + } + + @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false; + } + + @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getItemId() == R.id.remove) { + getView().onRemoveSelectedApps(); + return true; + } else if (item.getItemId() == R.id.selectAll) { + getView().onSelectAll(); + return true; + } + return false; + } + + @Override public void onDestroyActionMode(ActionMode mode) { + getView().onActionModeDestroyed(); + } + + @Override public void onRemoveSelectedApps(@Nullable List selections) { + if (selections != null && !selections.isEmpty()) { + for (AppsModel selection : selections) { + selection.delete(); + } + if (isAttached()) getView().onNotifyChanges(); + } + } + + @Override public void onSaveIndexChanges(@NonNull DeviceAppsAdapter adapter, int fromPosition, int toPosition) { + AppsModel fromTo = adapter.getItem(toPosition); + AppsModel toFrom = adapter.getItem(fromPosition); + int actualFrom = fromTo.getIndexPosition(); + int actualTo = toFrom.getIndexPosition(); + fromTo.setIndexPosition(actualTo); + fromTo.save(); + toFrom.setIndexPosition(actualFrom); + toFrom.save(); + } + + @Override public void onItemMove(int fromPosition, int toPosition) { + if (fromPosition < toPosition) { + for (int i = fromPosition; i < toPosition; i++) { + if (isAttached()) getView().onSwap(i, i + 1); + } + } else { + for (int i = fromPosition; i > toPosition; i--) { + if (isAttached()) getView().onSwap(i, i - 1); + } + } + if (isAttached()) getView().onNotifyItemMoved(fromPosition, toPosition); + } + + @Override public void onItemDismiss(int position) {} + + @Override public void onItemStoppedMoving() { + if (isAttached()) getView().onNotifyChanges(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsView.java b/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsView.java new file mode 100644 index 0000000..96e89ff --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/apps/selected/SelectedAppsView.java @@ -0,0 +1,178 @@ +package com.fastaccess.ui.modules.apps.selected; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.support.v4.widget.NestedScrollView; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.view.ActionMode; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.View; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.data.dao.FloatingEventModel; +import com.fastaccess.data.dao.SelectedAppsEventModel; +import com.fastaccess.data.dao.ThemePackEventModel; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.ui.adapter.DeviceAppsAdapter; +import com.fastaccess.ui.base.BaseFragment; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; +import com.fastaccess.ui.widgets.recyclerview.touch.SimpleItemTouchHelperCallback; +import com.mikhaellopez.circularfillableloaders.CircularFillableLoaders; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +import butterknife.BindView; +import icepick.State; + +/** + * Created by Kosh on 10 Oct 2016, 11:47 PM + */ + +public class SelectedAppsView extends BaseFragment implements SelectedAppsMvp.View { + public static final String TAG = "SelectedAppsView"; + + @BindView(R.id.recycler) DynamicRecyclerView recycler; + @BindView(R.id.empty_text) FontTextView emptyText; + @BindView(R.id.empty) NestedScrollView empty; + @BindView(R.id.progressBar) CircularFillableLoaders progressBar; + @State HashMap selection = new LinkedHashMap<>(); + private SelectedAppsPresenter presenter; + private DeviceAppsAdapter adapter; + private ActionMode actionMode; + private Loader loader; + + public static SelectedAppsView newInstance() { + return new SelectedAppsView(); + } + + @Override public void onAttach(Context context) { + super.onAttach(context); + EventBus.getDefault().register(this); + } + + @Override public void onDetach() { + super.onDetach(); + EventBus.getDefault().unregister(this); + } + + @Override protected int fragmentLayout() { + return R.layout.grid_list; + } + + @NonNull @Override protected SelectedAppsPresenter getPresenter() { + if (presenter == null) { + presenter = SelectedAppsPresenter.with(this); + } + return presenter; + } + + @Override protected void onFragmentCreated(View view, @Nullable Bundle savedInstanceState) { + recycler.setEmptyView(empty); + emptyText.setText(R.string.no_apps_selected); + adapter = new DeviceAppsAdapter(new ArrayList(), getPresenter(), selection); + recycler.setAdapter(adapter); + loader = getLoaderManager().initLoader(0, null, getPresenter()); + if (!selection.isEmpty()) { + actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(getPresenter()); + actionMode.setTitle(getString(R.string.selected) + " ( " + adapter.selectionSize() + " )"); + } + ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(getPresenter(), false); + ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(callback); + mItemTouchHelper.attachToRecyclerView(recycler); + } + + @Override public void onStartLoading() { + recycler.showProgress(progressBar); + } + + @Override public void onAppsLoaded(@Nullable List data) { + recycler.hideProgress(progressBar); + if (data != null) adapter.insertItems(data); + else adapter.clear(); + } + + @Override public void onLoaderReset() { + recycler.hideProgress(progressBar); + adapter.clear(); + } + + @Override public void setSelection(@NonNull String packageName, int position) { + adapter.select(packageName, position, !adapter.isSelected(packageName)); + if (hasSelection()) { + if (actionMode == null) { + actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(getPresenter()); + } + actionMode.setTitle(getString(R.string.selected) + " ( " + adapter.selectionSize() + " )"); + } else { + actionMode.finish(); + } + } + + @Override public boolean hasSelection() { + return adapter.hasSelection(); + } + + @Override public void onActionModeDestroyed() { + adapter.clearSelection(); + actionMode = null; + } + + @Override public void onRemoveSelectedApps() { + getPresenter().onRemoveSelectedApps(adapter.getSelections()); + if (actionMode != null) actionMode.finish(); + } + + @Override public void onFilter(@Nullable String text) { + if (progressBar.isShown()) { + return; + } + if (InputHelper.isEmpty(text)) { + loader.onContentChanged(); + } else { + adapter.getFilter().filter(text); + } + } + + @Override public void onNotifyChanges() { + if (loader != null) loader.onContentChanged();//notify changes + EventBus.getDefault().post(new FloatingEventModel()); + } + + @Override public void onNotifyItemMoved(int fromPosition, int toPosition) { + adapter.notifyItemMoved(fromPosition, toPosition); + } + + @Override public void onSwap(int fromPosition, int toPosition) { + getPresenter().onSaveIndexChanges(adapter, fromPosition, toPosition); + Collections.swap(adapter.getData(), fromPosition, toPosition); + } + + @Override public void onSelectAll() { + adapter.clearSelection(); + for (int i = 0; i < adapter.getData().size(); i++) { + AppsModel model = adapter.getItem(i); + setSelection(model.getComponentName().toShortString(), i); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(SelectedAppsEventModel eventModel) { + onNotifyChanges(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(ThemePackEventModel model) { + if (loader != null) loader.forceLoad(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginMvp.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginMvp.java new file mode 100644 index 0000000..4db8482 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginMvp.java @@ -0,0 +1,50 @@ +package com.fastaccess.ui.modules.cloud.auth; + +import android.content.Intent; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; + +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; + +/** + * Created by Kosh on 23 Oct 2016, 7:33 PM + */ + +public interface LoginMvp { + + interface View { + void onShowProgress(); + + void onHideProgress(); + + void onShowMessage(@StringRes int resId); + + void onShowMessage(@NonNull String msg); + + void onConnected(); + + void onConnectionFailed(@NonNull ConnectionResult connectionResult); + + void onSignedIn(@NonNull GoogleSignInAccount account); + + + void onFirebaseUser(@NonNull FirebaseUser user); + } + + interface Presenter extends GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, + FirebaseAuth.AuthStateListener, OnCompleteListener { + void onSignIn(@NonNull LoginView loginView, @NonNull GoogleApiClient googleApiClient); + + void onActivityResult(int requestCode, int resultCode, Intent data); + + void onFirebaseSignIn(@NonNull GoogleSignInAccount account, @NonNull FirebaseAuth auth); + + void onStartBackOrRestore(int type, @NonNull LoginView loginView); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginPresenter.java new file mode 100644 index 0000000..a379bbb --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginPresenter.java @@ -0,0 +1,117 @@ +package com.fastaccess.ui.modules.cloud.auth; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.fastaccess.R; +import com.fastaccess.helper.Logger; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; +import com.fastaccess.ui.modules.cloud.backup.BackupView; +import com.fastaccess.ui.modules.cloud.restore.RestoreView; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthCredential; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.GoogleAuthProvider; + +/** + * Created by Kosh on 23 Oct 2016, 7:49 PM + */ + +public class LoginPresenter extends BasePresenter implements LoginMvp.Presenter { + + private static final int SIGN_IN_REQUEST_CODE = 100; + + protected LoginPresenter(@NonNull LoginMvp.View view) { + super(view); + } + + public static LoginPresenter with(@NonNull LoginMvp.View view) { + return new LoginPresenter(view); + } + + @Override public void onSignIn(@NonNull LoginView loginView, @NonNull GoogleApiClient googleApiClient) { + Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient); + loginView.startActivityForResult(signInIntent, SIGN_IN_REQUEST_CODE); + loginView.onShowProgress(); + } + + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == SIGN_IN_REQUEST_CODE) { + if (isAttached()) { + GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); + if (result.isSuccess()) { + GoogleSignInAccount account = result.getSignInAccount(); + if (account != null) { + getView().onSignedIn(account); + } else { + getView().onShowMessage(R.string.failed_login); + } + } else { + getView().onShowMessage(R.string.failed_login); + } + } + } + + } + + @Override public void onFirebaseSignIn(@NonNull GoogleSignInAccount acct, @NonNull FirebaseAuth auth) { + AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null); + auth.signInWithCredential(credential).addOnCompleteListener(this); + } + + @Override public void onStartBackOrRestore(int type, @NonNull LoginView loginView) { + Intent intent = new Intent(loginView, type == LoginView.BACKUP_TYPE ? BackupView.class : RestoreView.class); + loginView.startActivity(intent); + loginView.finish(); + } + + @Override public void onConnected(@Nullable Bundle bundle) { + if (isAttached()) getView().onConnected(); + } + + @Override public void onConnectionSuspended(int i) { + //TODO + } + + @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + if (isAttached()) { + if (connectionResult.getErrorMessage() != null) { + getView().onShowMessage(connectionResult.getErrorMessage()); + } + getView().onConnectionFailed(connectionResult); + } + } + + @Override public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { + if (isAttached()) { + FirebaseUser user = firebaseAuth.getCurrentUser(); + if (user != null) { + getView().onFirebaseUser(user); + } else { + Logger.e(); + } + } + } + + @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @Override public void onComplete(@NonNull Task task) { + Logger.e(task.isSuccessful(), task.isComplete()); + if (isAttached()) { + if (!task.isSuccessful()) { + if (task.getException() != null && task.getException().getMessage() != null) { + getView().onShowMessage(task.getException().getMessage()); + } else { + getView().onShowMessage(R.string.failed_login); + } + } + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginView.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginView.java new file mode 100644 index 0000000..6c03f4c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/auth/LoginView.java @@ -0,0 +1,166 @@ +package com.fastaccess.ui.modules.cloud.auth; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.design.widget.Snackbar; +import android.view.View; +import android.widget.ProgressBar; + +import com.fastaccess.R; +import com.fastaccess.helper.Logger; +import com.fastaccess.ui.base.BaseActivity; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.SignInButton; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; + +import butterknife.BindView; +import butterknife.OnClick; +import icepick.State; + +/** + * Created by Kosh on 23 Oct 2016, 7:56 PM + */ + +public class LoginView extends BaseActivity implements LoginMvp.View { + + public static final int BACKUP_TYPE = 0; + public static final int RESTORE_TYPE = 1; + public static final String TYPE = "type"; + + @BindView(R.id.topProgress) ProgressBar topProgress; + @BindView(R.id.signInBtn) SignInButton signInBtn; + @State int type; + + private GoogleApiClient mGoogleApiClient; + private GoogleSignInOptions signInOptions; + private ProgressDialog progressDialog; + private LoginPresenter presenter; + private FirebaseAuth firebaseAuth; + + + @OnClick(R.id.signInBtn) void onSignIn() { + getPresenter().onSignIn(this, getGoogleApiClient()); + } + + @Override protected int layout() { + return R.layout.sign_in_layout; + } + + @NonNull @Override protected LoginPresenter getPresenter() { + if (presenter == null) presenter = LoginPresenter.with(this); + return presenter; + } + + @Override protected boolean isTransparent() { + return false; + } + + @Override protected boolean canBack() { + return true; + } + + @Override public void onShowProgress() { + topProgress.setVisibility(View.VISIBLE); + if (!getProgressDialog().isShowing()) getProgressDialog().show(); + } + + @Override public void onHideProgress() { + topProgress.setVisibility(View.GONE); + if (getProgressDialog().isShowing()) getProgressDialog().dismiss(); + } + + @Override public void onShowMessage(@StringRes int resId) { + onShowMessage(getString(resId)); + } + + @Override public void onShowMessage(@NonNull String msg) { + onHideProgress(); + Snackbar.make(signInBtn, msg, Snackbar.LENGTH_LONG).show(); + } + + @Override public void onConnected() { + Logger.e(); + } + + @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + Logger.e(); + } + + @Override public void onSignedIn(@NonNull GoogleSignInAccount account) { + getPresenter().onFirebaseSignIn(account, getFirebaseAuth()); + } + + @Override public void onFirebaseUser(@NonNull FirebaseUser user) { + onHideProgress(); + getPresenter().onStartBackOrRestore(type, this); + } + + @Override protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + type = getIntent().getExtras().getInt(TYPE); + } + setTitle(type == BACKUP_TYPE ? R.string.backup : R.string.restore); + signInBtn.setSize(SignInButton.SIZE_WIDE); + } + + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + getPresenter().onActivityResult(requestCode, resultCode, data); + } + + @Override protected void onStart() { + super.onStart(); + getFirebaseAuth().addAuthStateListener(getPresenter()); + } + + @Override protected void onStop() { + super.onStop(); + getFirebaseAuth().removeAuthStateListener(getPresenter()); + } + + private ProgressDialog getProgressDialog() { + if (progressDialog == null) { + progressDialog = new ProgressDialog(this); + progressDialog.setCancelable(false); + progressDialog.setMessage(getString(R.string.in_progress)); + } + return progressDialog; + } + + private GoogleApiClient getGoogleApiClient() { + if (mGoogleApiClient == null) { + mGoogleApiClient = new GoogleApiClient.Builder(this) + .enableAutoManage(this, getPresenter()) + .addApi(Auth.GOOGLE_SIGN_IN_API, getSignInOptions()) + .build(); + } + return mGoogleApiClient; + } + + private GoogleSignInOptions getSignInOptions() { + if (signInOptions == null) { + signInOptions = new GoogleSignInOptions + .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken(getString(R.string.default_web_client_id)) + .requestEmail() + .build(); + } + return signInOptions; + } + + public FirebaseAuth getFirebaseAuth() { + if (firebaseAuth == null) { + firebaseAuth = FirebaseAuth.getInstance(); + } + return firebaseAuth; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupMvp.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupMvp.java new file mode 100644 index 0000000..c386e63 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupMvp.java @@ -0,0 +1,32 @@ +package com.fastaccess.ui.modules.cloud.backup; + +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; + +import com.google.firebase.database.DatabaseReference; + +/** + * Created by Kosh on 23 Oct 2016, 8:56 PM + */ + +public interface BackupMvp { + + interface View { + void onShowProgress(); + + void onHideProgress(); + + void onShowMessage(@StringRes int resId); + + void onShowMessage(@NonNull String msg); + + void finishOnError(); + + void onBackupCompleted(); + } + + interface Presenter extends DatabaseReference.CompletionListener { + + void onBackup(DatabaseReference databaseReference); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupPresenter.java new file mode 100644 index 0000000..0d41a92 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupPresenter.java @@ -0,0 +1,46 @@ +package com.fastaccess.ui.modules.cloud.backup; + +import android.support.annotation.NonNull; + +import com.fastaccess.R; +import com.fastaccess.data.dao.BackupRestoreModel; +import com.fastaccess.helper.Logger; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; + +/** + * Created by Kosh on 23 Oct 2016, 9:04 PM + */ + +public class BackupPresenter extends BasePresenter implements BackupMvp.Presenter { + + protected BackupPresenter(@NonNull BackupMvp.View view) { + super(view); + } + + public static BackupPresenter with(@NonNull BackupMvp.View view) { + return new BackupPresenter(view); + } + + @Override public void onBackup(DatabaseReference databaseReference) { + BackupRestoreModel model = BackupRestoreModel.backup(); + if (model == null || model.getUid() == null) { + getView().onShowMessage(R.string.login_first_msg); + getView().finishOnError(); + } else { + getView().onShowProgress(); + Logger.e(model, model.getFolders(), model.getAppsModels()); + databaseReference.child(BackupView.BACKUP_DATABASE_NAME).child(model.getUid()).setValue(model, this); + } + } + + @Override public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) { + if (databaseError != null) { + getView().onShowMessage(databaseError.getDetails()); + getView().finishOnError(); + } else { + getView().onBackupCompleted(); + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupView.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupView.java new file mode 100644 index 0000000..7735ec9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/backup/BackupView.java @@ -0,0 +1,92 @@ +package com.fastaccess.ui.modules.cloud.backup; + +import android.app.ProgressDialog; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.widget.Toast; + +import com.fastaccess.R; +import com.fastaccess.ui.base.BaseActivity; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; + +/** + * Created by Kosh on 23 Oct 2016, 9:05 PM + */ + +public class BackupView extends BaseActivity implements BackupMvp.View { + public static final String BACKUP_DATABASE_NAME = "backup_db"; + + private BackupPresenter presenter; + private ProgressDialog progressDialog; + private DatabaseReference database; + + @Override protected int layout() { + return 0; + } + + @NonNull @Override protected BackupPresenter getPresenter() { + if (presenter == null) { + presenter = BackupPresenter.with(this); + } + return presenter; + } + + @Override protected boolean isTransparent() { + return false; + } + + @Override protected boolean canBack() { + return false; + } + + @Override public void onShowProgress() { + if (!getProgressDialog().isShowing()) getProgressDialog().show(); + } + + @Override public void onHideProgress() { + if (getProgressDialog().isShowing()) getProgressDialog().dismiss(); + } + + @Override public void onShowMessage(@StringRes int resId) { + onShowMessage(getString(resId)); + } + + @Override public void onShowMessage(@NonNull String msg) { + onHideProgress(); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + } + + @Override public void finishOnError() { + finish(); + } + + @Override public void onBackupCompleted() { + onHideProgress(); + onShowMessage(R.string.successfully_backup); + finish(); + } + + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getPresenter().onBackup(getDatabase()); + } + + private ProgressDialog getProgressDialog() { + if (progressDialog == null) { + progressDialog = new ProgressDialog(this); + progressDialog.setCancelable(false); + progressDialog.setMessage(getString(R.string.backup_in_progress)); + } + return progressDialog; + } + + private DatabaseReference getDatabase() { + if (database == null) { + database = FirebaseDatabase.getInstance().getReference(); + } + return database; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestoreMvp.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestoreMvp.java new file mode 100644 index 0000000..b8d91bd --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestoreMvp.java @@ -0,0 +1,37 @@ +package com.fastaccess.ui.modules.cloud.restore; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; + +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.ValueEventListener; + +/** + * Created by Kosh on 23 Oct 2016, 8:56 PM + */ + +public interface RestoreMvp { + + interface View { + void onShowProgress(); + + void onHideProgress(); + + void onShowMessage(@StringRes int resId); + + void onShowMessage(@NonNull String msg); + + void finishOnError(); + + void onRestoreCompleted(); + + @Nullable FirebaseUser user(); + } + + interface Presenter extends ValueEventListener { + + void onRestore(DatabaseReference databaseReference); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestorePresenter.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestorePresenter.java new file mode 100644 index 0000000..e54499c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestorePresenter.java @@ -0,0 +1,74 @@ +package com.fastaccess.ui.modules.cloud.restore; + +import android.support.annotation.NonNull; + +import com.fastaccess.R; +import com.fastaccess.data.dao.BackupRestoreModel; +import com.fastaccess.helper.Logger; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; +import com.fastaccess.ui.modules.cloud.backup.BackupView; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.Query; + +/** + * Created by Kosh on 23 Oct 2016, 9:04 PM + */ + +public class RestorePresenter extends BasePresenter implements RestoreMvp.Presenter { + + protected RestorePresenter(@NonNull RestoreMvp.View view) { + super(view); + } + + public static RestorePresenter with(@NonNull RestoreMvp.View view) { + return new RestorePresenter(view); + } + + @Override public void onRestore(DatabaseReference databaseReference) { + FirebaseUser user = getView().user(); + if (user == null) { + getView().onShowMessage(R.string.login_first_msg); + } else { + getView().onShowProgress(); + Logger.e(user.getUid()); + Query query = databaseReference + .child(BackupView.BACKUP_DATABASE_NAME) + .child(user.getUid()) + .limitToFirst(1); + query.keepSynced(true); + query.addListenerForSingleValueEvent(this); + } + } + + @Override public void onDataChange(DataSnapshot dataSnapshot) { + if (!isAttached()) return; + if (dataSnapshot != null) { + Logger.e(dataSnapshot); + FirebaseUser user = getView().user(); + if (user == null) { + getView().onHideProgress(); + getView().onShowMessage(R.string.login_first_msg); + getView().finishOnError(); + return; + } + if (dataSnapshot.hasChildren()) { + BackupRestoreModel.restore(dataSnapshot.getValue(BackupRestoreModel.class)); + } + getView().onHideProgress(); + getView().onRestoreCompleted(); + } else { + getView().onHideProgress(); + getView().onShowMessage(R.string.no_data_to_restore); + getView().finishOnError(); + } + } + + @Override public void onCancelled(DatabaseError databaseError) { + Logger.e(databaseError.getMessage()); + getView().onHideProgress(); + getView().finishOnError(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestoreView.java b/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestoreView.java new file mode 100644 index 0000000..b214b78 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/cloud/restore/RestoreView.java @@ -0,0 +1,105 @@ +package com.fastaccess.ui.modules.cloud.restore; + +import android.app.ProgressDialog; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.widget.Toast; + +import com.fastaccess.R; +import com.fastaccess.ui.base.BaseActivity; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; + +/** + * Created by Kosh on 23 Oct 2016, 9:05 PM + */ + +public class RestoreView extends BaseActivity implements RestoreMvp.View { + private RestorePresenter presenter; + private ProgressDialog progressDialog; + private DatabaseReference database; + private FirebaseUser user; + + @Override protected int layout() { + return 0; + } + + @NonNull @Override protected RestorePresenter getPresenter() { + if (presenter == null) { + presenter = RestorePresenter.with(this); + } + return presenter; + } + + @Override protected boolean isTransparent() { + return false; + } + + @Override protected boolean canBack() { + return false; + } + + @Override public void onShowProgress() { + if (!getProgressDialog().isShowing()) getProgressDialog().show(); + } + + @Override public void onHideProgress() { + if (getProgressDialog().isShowing()) getProgressDialog().dismiss(); + } + + @Override public void onShowMessage(@StringRes int resId) { + onShowMessage(getString(resId)); + } + + @Override public void onShowMessage(@NonNull String msg) { + onHideProgress(); + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); + } + + @Override public void finishOnError() { + finish(); + } + + @Override public void onRestoreCompleted() { + onHideProgress(); + onShowMessage(R.string.successfully_restored); + finish(); + } + + @Nullable @Override public FirebaseUser user() { + if (user == null) user = FirebaseAuth.getInstance().getCurrentUser(); + return user; + } + + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getPresenter().onRestore(getDatabase()); + } + + @Override protected void onStop() { + super.onStop(); + try { + getDatabase().removeEventListener(getPresenter()); + } catch (Exception ignored) {} + } + + private ProgressDialog getProgressDialog() { + if (progressDialog == null) { + progressDialog = new ProgressDialog(this); + progressDialog.setCancelable(false); + progressDialog.setMessage(getString(R.string.restore_in_progress)); + } + return progressDialog; + } + + private DatabaseReference getDatabase() { + if (database == null) { + database = FirebaseDatabase.getInstance().getReference(); + } + return database; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingMvp.java b/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingMvp.java new file mode 100644 index 0000000..38733df --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingMvp.java @@ -0,0 +1,71 @@ +package com.fastaccess.ui.modules.floating; + +import android.graphics.Point; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.view.WindowManager; + +import com.fastaccess.ui.widgets.floating.FloatingTouchCallback; +import com.fastaccess.ui.widgets.floating.FloatingView; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.List; + +/** + * Created by Kosh on 23 Oct 2016, 12:14 AM + */ + +public interface BaseFloatingMvp { + + interface BaseView { + + Loader getLoader(); + + BaseRecyclerAdapter getAdapter(); + + BaseFloatingPresenter> getPresenter(); + + void onLoaderLoaded(@Nullable List data); + + void onViewMoving(int x, int y); + + void onStoppedMoving(); + + void onLongPressed(); + + void onDoubleTapped(); + + void onSingleTapped(); + + void onTouchedOutside(); + + void onBackPressed(); + + void onDestroy(); + + void onToggleVisibility(boolean showFloating); + + void onConfigChanged(int orientation); + + void onUpdateXY(); + + void setupParamsSize(); + } + + interface BasePresenter> extends BaseViewHolder.OnItemClickListener, + FloatingTouchCallback, Loader.OnLoadCompleteListener> { + void onUpdateWindowParams(@NonNull WindowManager windowManager, + @NonNull WindowManager.LayoutParams originalParams, + @NonNull FloatingView floatingView, + int x, int y); + + void onToggleVisibility(boolean showFloating, @NonNull WindowManager windowManager, + @NonNull WindowManager.LayoutParams originalParams, @NonNull android.view.View view, + @NonNull FloatingView floatingView, boolean isHorizontal); + + void onMoveToEdge(@NonNull WindowManager windowManager, @NonNull WindowManager.LayoutParams originalParams, + @NonNull FloatingView floatingView, @NonNull Point szWindow); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingPresenter.java new file mode 100644 index 0000000..7c21b15 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingPresenter.java @@ -0,0 +1,132 @@ +package com.fastaccess.ui.modules.floating; + +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import android.graphics.Point; +import android.support.annotation.NonNull; +import android.support.v4.content.Loader; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.AccelerateInterpolator; + +import com.fastaccess.R; +import com.fastaccess.helper.AnimHelper; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.helper.ViewHelper; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; +import com.fastaccess.ui.widgets.floating.FloatingView; +import com.fastaccess.ui.widgets.recyclerview.DynamicRecyclerView; + +import java.util.List; + +/** + * Created by Kosh on 23 Oct 2016, 12:18 AM + */ + +public class BaseFloatingPresenter> extends BasePresenter implements BaseFloatingMvp.BasePresenter { + + private final TimeInterpolator moveEdgeInterpolator = new AccelerateInterpolator(); + + protected BaseFloatingPresenter(@NonNull V view) { + super(view); + } + + @Override public void onViewMoving(int x, int y) { + if (isAttached()) getView().onViewMoving(x, y); + } + + @Override public void onSingleTapped() { + if (isAttached()) getView().onSingleTapped(); + } + + @Override public void onDoubleTapped() { + if (isAttached()) getView().onDoubleTapped(); + } + + @Override public void onLongPressed() { + if (isAttached()) getView().onLongPressed(); + } + + @Override public void onSwipe(int swipeDirection) {}//Op-out + + @Override public void onBackPressed() { + onTouchOutside(); + } + + @Override public void onTouchOutside() { + if (isAttached()) getView().onTouchedOutside(); + } + + @Override public void onStoppedMoving() { + if (isAttached()) getView().onStoppedMoving(); + } + + @Override public void onConfigChanged(int orientation) { + if (isAttached()) getView().onConfigChanged(orientation); + } + + @Override public void onItemClick(int position, View v, M item) { + if (isAttached()) getView().onTouchedOutside(); + } + + @Override public void onItemLongClick(int position, View v, M item) { + onItemClick(position, v, item); + } + + @Override public void onUpdateWindowParams(@NonNull WindowManager windowManager, + @NonNull WindowManager.LayoutParams originalParams, + @NonNull FloatingView floatingView, + int x, int y) { + originalParams.x = x; + originalParams.y = y; + windowManager.updateViewLayout(floatingView, originalParams); + } + + @Override public void onToggleVisibility(final boolean showFloating, @NonNull final WindowManager windowManager, + @NonNull final WindowManager.LayoutParams originalParams, @NonNull final View view, + @NonNull final FloatingView floatingView, boolean isHorizontal) { + AnimHelper.animateVisibility(floatingView, showFloating); + AnimHelper.animateVisibility(view, !showFloating, showFloating ? new AnimHelper.AnimationCallback() { + @Override public void onAnimationEnd() { + if (!isAttached()) return; + getView().setupParamsSize(); + } + + @Override public void onAnimationStart() {} + } : null); + if (!showFloating) { + if (!isAttached()) return; + if (isHorizontal) { + final DynamicRecyclerView recycler = (DynamicRecyclerView) view.findViewById(R.id.recycler); + originalParams.width = ViewHelper.getWidthFromRecyclerView(recycler, windowManager); + } + windowManager.updateViewLayout(view, originalParams); + } + } + + @Override public void onMoveToEdge(@NonNull final WindowManager windowManager, @NonNull final WindowManager.LayoutParams originalParams, + @NonNull final FloatingView floatingView, @NonNull Point szWindow) { + if (!PrefHelper.getBoolean(PrefConstant.FA_EDGES_STICKY)) return; + int w = originalParams.width; + final boolean isMoveRightEdge = originalParams.x + w / 2 <= szWindow.x / 2; + final int goalPositionX = isMoveRightEdge ? 0 : szWindow.x - w; + if (!isAttached() || !floatingView.isShown()) return; + ValueAnimator mMoveEdgeAnimator = ValueAnimator.ofInt(originalParams.x, goalPositionX); + mMoveEdgeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override public void onAnimationUpdate(ValueAnimator animation) { + if (!isAttached() || !floatingView.isShown()) return; + originalParams.x = (Integer) animation.getAnimatedValue(); + windowManager.updateViewLayout(floatingView, originalParams); + if (isAttached()) getView().onUpdateXY(); + } + }); + mMoveEdgeAnimator.setDuration(200L); + mMoveEdgeAnimator.setInterpolator(moveEdgeInterpolator); + mMoveEdgeAnimator.start(); + } + + @Override public void onLoadComplete(Loader> loader, List data) { + if (isAttached()) getView().onLoaderLoaded(data); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingView.java b/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingView.java new file mode 100644 index 0000000..fb36e84 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/BaseFloatingView.java @@ -0,0 +1,176 @@ +package com.fastaccess.ui.modules.floating; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.graphics.Point; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.GravityCompat; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.WindowManager; + +import com.fastaccess.R; +import com.fastaccess.data.dao.FloatingEventModel; +import com.fastaccess.data.dao.ThemePackEventModel; +import com.fastaccess.helper.NotificationHelper; +import com.fastaccess.helper.PermissionsHelper; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.provider.service.FloatingService; +import com.fastaccess.ui.adapter.viewholder.FloatingWindowsViewHolder; +import com.fastaccess.ui.widgets.floating.FloatingView; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.util.List; + +import static android.graphics.PixelFormat.TRANSLUCENT; +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; + +/** + * Created by Kosh on 23 Oct 2016, 12:36 AM + */ + +public abstract class BaseFloatingView implements BaseFloatingMvp.BaseView { + private WindowManager.LayoutParams originalParams; + protected WindowManager windowManager; + protected Context context; + protected FloatingView floatingView; + private FloatingWindowsViewHolder layoutHolder; + private Point szWindow = new Point(); + protected boolean isHorizontal; + + @SuppressWarnings("unused") private BaseFloatingView() { + throw new RuntimeException("can't call me!"); + } + + @SuppressLint("InflateParams") protected BaseFloatingView(@NonNull Context context, boolean isHorizontal) { + EventBus.getDefault().register(this); + this.isHorizontal = isHorizontal; + this.context = context; + if (!PermissionsHelper.isSystemAlertGranted(context)) { + context.stopService(new Intent(context, FloatingService.class)); + return; + } + windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + windowManager.getDefaultDisplay().getSize(szWindow); + originalParams = new WindowManager.LayoutParams(TYPE_PRIORITY_PHONE, FLAG_NOT_TOUCH_MODAL | + FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_FOCUSABLE, TRANSLUCENT); + setupParamsSize(); + boolean isAutoSavePosition = PrefHelper.getBoolean(PrefConstant.FA_AUTO_SAVE_POSITION); + originalParams.x = isAutoSavePosition ? PrefHelper.getInt(PrefConstant.POSITION_X) : 0; + originalParams.y = isAutoSavePosition ? PrefHelper.getInt(PrefConstant.POSITION_Y) : 100; + originalParams.gravity = GravityCompat.START | Gravity.TOP; + floatingView = new FloatingView(context, getPresenter()); + onUpdateXY(); + windowManager.addView(floatingView, originalParams); + layoutHolder = new FloatingWindowsViewHolder(LayoutInflater.from(context).inflate(isHorizontal ? R.layout.horizontal_layout : R.layout + .vertical_layout, null, false), this); + layoutHolder.recycler.setAdapter(getAdapter()); + windowManager.addView(layoutHolder.tabBar, originalParams); + getLoader(); + moveToEdge(); + } + + @Override public void onLoaderLoaded(@Nullable List data) { + if (data == null || data.isEmpty()) { + getAdapter().clear(); + context.stopService(new Intent(context, FloatingService.class)); + } else { + getAdapter().insertItems(data); + } + } + + @Override public void onViewMoving(int x, int y) { + getPresenter().onUpdateWindowParams(windowManager, originalParams, floatingView, x, y); + } + + @Override public void onStoppedMoving() { + moveToEdge(); + onUpdateXY(); + PrefConstant.savePosition(originalParams.x, originalParams.y); + } + + @Override public void onLongPressed() { + NotificationHelper.collapseFAService(context, getAdapter().getItemCount()); + } + + @Override public void onDoubleTapped() { + Intent startMain = new Intent(Intent.ACTION_MAIN); + startMain.addCategory(Intent.CATEGORY_HOME); + startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(startMain); + } + + @Override public void onSingleTapped() { + onToggleVisibility(false); + } + + @Override public void onTouchedOutside() { + if (layoutHolder.tabBar.isShown() && !PrefHelper.getBoolean(PrefConstant.FA_ALWAYS_SHOWING)) { + onToggleVisibility(true); + } + } + + @Override public void onBackPressed() { + onTouchedOutside(); + } + + @Override public void onDestroy() { + if (windowManager != null) { + windowManager.removeView(floatingView); + windowManager.removeView(layoutHolder.tabBar); + } + if (getLoader() != null) getLoader().unregisterListener(getPresenter()); + if (getAdapter() != null) getAdapter().clear(); + if (layoutHolder != null) layoutHolder.onDestroy(); + if (getPresenter() != null) getPresenter().onDestroy(); + EventBus.getDefault().unregister(this); + } + + @Override public void onToggleVisibility(boolean showFloating) { + getPresenter().onToggleVisibility(showFloating, windowManager, originalParams, layoutHolder.tabBar, floatingView, isHorizontal); + } + + @Override public void onConfigChanged(int orientation) { + windowManager.getDefaultDisplay().getSize(szWindow); + moveToEdge(); + } + + @Override public void onUpdateXY() { + floatingView.setInitialX(originalParams.x); + floatingView.setInitialY(originalParams.y); + } + + @SuppressWarnings("unused") @Subscribe public void onEvent(FloatingEventModel model) { + if (!model.isSettingsChanged()) { + if (getLoader() != null) getLoader().onContentChanged(); + } else { + setupParamsSize(); + floatingView.setupImageView(); + layoutHolder.onSetupBackground(); + moveToEdge(); + } + } + + @SuppressWarnings("unused") @Subscribe public void onEvent(ThemePackEventModel model) { + if (getLoader() != null) getLoader().forceLoad(); + } + + @Override public void setupParamsSize() { + originalParams.width = PrefConstant.getFinalSize(context); + originalParams.height = isHorizontal ? PrefConstant.getFinalSize(context) : WRAP_CONTENT; + if (floatingView != null) windowManager.updateViewLayout(floatingView, originalParams); + } + + private void moveToEdge() { + getPresenter().onMoveToEdge(windowManager, originalParams, floatingView, szWindow); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingHVMvp.java b/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingHVMvp.java new file mode 100644 index 0000000..90769df --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingHVMvp.java @@ -0,0 +1,13 @@ +package com.fastaccess.ui.modules.floating.apps; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.modules.floating.BaseFloatingMvp; + +/** + * Created by Kosh on 14 Oct 2016, 8:54 PM + */ + +public interface FloatingHVMvp { + + interface Presenter extends BaseFloatingMvp.BasePresenter> {} +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingVHPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingVHPresenter.java new file mode 100644 index 0000000..d6de18f --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingVHPresenter.java @@ -0,0 +1,41 @@ +package com.fastaccess.ui.modules.floating.apps; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; +import android.view.View; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.modules.floating.BaseFloatingMvp; +import com.fastaccess.ui.modules.floating.BaseFloatingPresenter; + +/** + * Created by Kosh on 14 Oct 2016, 9:00 PM + */ + +public class FloatingVHPresenter extends BaseFloatingPresenter> implements FloatingHVMvp.Presenter { + + + public FloatingVHPresenter(@NonNull BaseFloatingMvp.BaseView view) { + super(view); + } + + public static FloatingVHPresenter with(@NonNull BaseFloatingMvp.BaseView view) { + return new FloatingVHPresenter(view); + } + + @Override public void onItemClick(int position, View v, AppsModel item) { + try { + Context context = v.getContext(); + PackageManager manager = context.getPackageManager(); + Intent intent = manager.getLaunchIntentForPackage(item.getPackageName()); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + context.startActivity(intent); + } catch (Exception e) {// app uninstalled/not found + e.printStackTrace(); + item.delete(); + } + super.onItemClick(position, v, item); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingVHView.java b/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingVHView.java new file mode 100644 index 0000000..a5e5d44 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/apps/FloatingVHView.java @@ -0,0 +1,56 @@ +package com.fastaccess.ui.modules.floating.apps; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v4.content.Loader; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.provider.loader.SelectedAppsLoader; +import com.fastaccess.ui.adapter.FloatingAppsAdapter; +import com.fastaccess.ui.adapter.viewholder.FloatingAppsViewHolder; +import com.fastaccess.ui.modules.floating.BaseFloatingView; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.ArrayList; + +/** + * Created by Kosh on 14 Oct 2016, 9:12 PM + */ + + +public class FloatingVHView extends BaseFloatingView { + private FloatingAppsAdapter adapter; + private SelectedAppsLoader appsLoader; + private FloatingVHPresenter presenter; + + protected FloatingVHView(@NonNull Context context, boolean isHorizontal) { + super(context, isHorizontal); + } + + public static FloatingVHView with(@NonNull Context context, boolean isHorizontal) { + return new FloatingVHView(context, isHorizontal); + } + + @Override public Loader getLoader() { + if (appsLoader == null) { + appsLoader = new SelectedAppsLoader(context); + appsLoader.registerListener(10, getPresenter()); + appsLoader.startLoading(); + } + return appsLoader; + } + + @Override public BaseRecyclerAdapter> getAdapter() { + if (adapter == null) { + adapter = new FloatingAppsAdapter(new ArrayList(), getPresenter(), isHorizontal); + } + return adapter; + } + + @Override public FloatingVHPresenter getPresenter() { + if (presenter == null) presenter = new FloatingVHPresenter(this); + return presenter; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersMvp.java b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersMvp.java new file mode 100644 index 0000000..dc0c4fc --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersMvp.java @@ -0,0 +1,21 @@ +package com.fastaccess.ui.modules.floating.folders; + +import android.support.annotation.NonNull; + +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.ui.modules.floating.BaseFloatingMvp; + +/** + * Created by Kosh on 14 Oct 2016, 8:54 PM + */ + +public interface FloatingFoldersMvp { + + interface View extends BaseFloatingMvp.BaseView { + void onOpenFolder(@NonNull android.view.View v, @NonNull FolderModel item); + } + + interface Presenter extends BaseFloatingMvp.BasePresenter { + + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersPresenter.java new file mode 100644 index 0000000..999d265 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersPresenter.java @@ -0,0 +1,28 @@ +package com.fastaccess.ui.modules.floating.folders; + +import android.support.annotation.NonNull; +import android.view.View; + +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.ui.modules.floating.BaseFloatingPresenter; + +/** + * Created by Kosh on 14 Oct 2016, 9:00 PM + */ + +public class FloatingFoldersPresenter extends BaseFloatingPresenter implements FloatingFoldersMvp + .Presenter { + + private FloatingFoldersPresenter(@NonNull FloatingFoldersMvp.View view) { + super(view); + } + + public static FloatingFoldersPresenter with(@NonNull FloatingFoldersMvp.View view) { + return new FloatingFoldersPresenter(view); + } + + @Override public void onItemClick(int position, View v, FolderModel item) { + super.onItemClick(position, v, item); + if (isAttached()) getView().onOpenFolder(v, item); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersView.java b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersView.java new file mode 100644 index 0000000..0e9e068 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/FloatingFoldersView.java @@ -0,0 +1,70 @@ +package com.fastaccess.ui.modules.floating.folders; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.v4.content.Loader; +import android.view.View; + +import com.fastaccess.data.dao.FolderEventModel; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.provider.loader.FoldersLoader; +import com.fastaccess.ui.adapter.FloatingFoldersAdapter; +import com.fastaccess.ui.modules.floating.BaseFloatingMvp; +import com.fastaccess.ui.modules.floating.BaseFloatingPresenter; +import com.fastaccess.ui.modules.floating.BaseFloatingView; +import com.fastaccess.ui.modules.floating.folders.drawer.FloatingDrawerView; +import com.fastaccess.ui.widgets.recyclerview.BaseRecyclerAdapter; + +import org.greenrobot.eventbus.Subscribe; + +import java.util.ArrayList; + +/** + * Created by Kosh on 14 Oct 2016, 9:12 PM + */ + + +public class FloatingFoldersView extends BaseFloatingView implements FloatingFoldersMvp.View { + private FloatingFoldersPresenter presenter; + private FoldersLoader foldersLoader; + private FloatingFoldersAdapter adapter; + + private FloatingFoldersView(@NonNull Context context, boolean isHorizontal) { + super(context, isHorizontal); + } + + public static FloatingFoldersView with(@NonNull Context context, boolean isHorizontal) { + return new FloatingFoldersView(context, isHorizontal); + } + + @SuppressWarnings("unused") @Subscribe public void onEvent(FolderEventModel model) { + if (foldersLoader != null) foldersLoader.onContentChanged(); + } + + @Override public void onOpenFolder(@NonNull View v, @NonNull FolderModel item) { + FloatingDrawerView.with(this).onShow(windowManager, floatingView, item); + } + + @Override public Loader getLoader() { + if (foldersLoader == null) { + foldersLoader = new FoldersLoader(context); + foldersLoader.registerListener(10, getPresenter()); + foldersLoader.startLoading(); + } + return foldersLoader; + } + + @Override public BaseRecyclerAdapter getAdapter() { + if (adapter == null) { + adapter = new FloatingFoldersAdapter(new ArrayList(), getPresenter(), isHorizontal); + } + return adapter; + } + + @Override public BaseFloatingPresenter> getPresenter() { + if (presenter == null) { + presenter = FloatingFoldersPresenter.with(this); + } + return presenter; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawPresenter.java new file mode 100644 index 0000000..2cee50d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawPresenter.java @@ -0,0 +1,50 @@ +package com.fastaccess.ui.modules.floating.folders.drawer; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; +import android.support.v4.content.Loader; +import android.view.View; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +import java.util.List; + +/** + * Created by Kosh on 22 Oct 2016, 3:13 PM + */ + +public class FloatingDrawPresenter extends BasePresenter implements FloatingDrawerMvp.Presenter { + + protected FloatingDrawPresenter(@NonNull FloatingDrawerMvp.View view) { + super(view); + } + + public static FloatingDrawPresenter with(@NonNull FloatingDrawerMvp.View view) { + return new FloatingDrawPresenter(view); + } + + @Override public void onLoadComplete(Loader> loader, List data) { + if (isAttached()) getView().onAppsLoaded(data); + } + + @Override public void onItemClick(int position, View v, AppsModel item) { + try { + Context context = v.getContext(); + PackageManager manager = context.getPackageManager(); + Intent intent = manager.getLaunchIntentForPackage(item.getPackageName()); + intent.addCategory(Intent.CATEGORY_LAUNCHER); + context.startActivity(intent); + } catch (Exception e) {// app uninstalled/not found + e.printStackTrace(); + item.delete(); + } + if (isAttached()) getView().onTouchedOutside(); + } + + @Override public void onItemLongClick(int position, View v, AppsModel item) { + onItemClick(position, v, item); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawerMvp.java b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawerMvp.java new file mode 100644 index 0000000..8c6eb60 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawerMvp.java @@ -0,0 +1,37 @@ +package com.fastaccess.ui.modules.floating.folders.drawer; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.content.Loader; +import android.view.WindowManager; + +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.ui.widgets.recyclerview.BaseViewHolder; + +import java.util.List; + +/** + * Created by Kosh on 22 Oct 2016, 3:11 PM + */ + +public interface FloatingDrawerMvp { + + interface View { + + void onShow(@NonNull WindowManager windowManager, @NonNull android.view.View view, @NonNull FolderModel folder); + + void onAppsLoaded(@Nullable List models); + + void onConfigChanged(int orientation); + + void onTouchedOutside(); + + void onBackPressed(); + + void onDestroy(); + } + + interface Presenter extends BaseViewHolder.OnItemClickListener, + Loader.OnLoadCompleteListener> {} +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawerView.java b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawerView.java new file mode 100644 index 0000000..b12779a --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/floating/folders/drawer/FloatingDrawerView.java @@ -0,0 +1,145 @@ +package com.fastaccess.ui.modules.floating.folders.drawer; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.NinePatchDrawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.WindowManager; + +import com.fastaccess.R; +import com.fastaccess.data.dao.AppsModel; +import com.fastaccess.data.dao.FolderModel; +import com.fastaccess.data.dao.ThemePackEventModel; +import com.fastaccess.helper.ViewHelper; +import com.fastaccess.provider.loader.SelectedAppsLoader; +import com.fastaccess.ui.adapter.FloatingAppsAdapter; +import com.fastaccess.ui.adapter.viewholder.AppDrawerHolder; +import com.fastaccess.ui.modules.floating.folders.FloatingFoldersMvp; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.util.ArrayList; +import java.util.List; + +import static android.graphics.PixelFormat.TRANSLUCENT; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; +import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE; + +/** + * Created by Kosh on 22 Oct 2016, 11:45 AM + */ + +public class FloatingDrawerView implements FloatingDrawerMvp.View { + + private WindowManager.LayoutParams originalParams; + private final FloatingFoldersMvp.View view; + private AppDrawerHolder drawerHolder; + private SelectedAppsLoader appsLoader; + private FloatingAppsAdapter adapter; + private WindowManager windowManager; + private FloatingDrawPresenter presenter; + + + private FloatingDrawerView(@NonNull FloatingFoldersMvp.View view) { + this.view = view; + EventBus.getDefault().register(this); + } + + public static FloatingDrawerView with(@NonNull FloatingFoldersMvp.View view) { + return new FloatingDrawerView(view); + } + + private void setupParams(@NonNull WindowManager windowManager) { + this.windowManager = windowManager; + originalParams = new WindowManager.LayoutParams(TYPE_PRIORITY_PHONE, + FLAG_WATCH_OUTSIDE_TOUCH | FLAG_NOT_TOUCH_MODAL, TRANSLUCENT); + Point szWindow = new Point(); + windowManager.getDefaultDisplay().getSize(szWindow); + updateParams(ViewHelper.isLandscape(drawerHolder.appDrawer.getResources()) ? 2 : 1, false); + originalParams.gravity = Gravity.CENTER; + windowManager.addView(drawerHolder.appDrawer, originalParams); + drawerHolder.appDrawer.animate().scaleX(1f).scaleY(1f); + } + + private void updateParams(int orientation, boolean update) { + Point szWindow = new Point(); + windowManager.getDefaultDisplay().getSize(szWindow); +// originalParams.width = MATCH_PARENT; +// originalParams.height = WRAP_CONTENT; + originalParams.width = orientation == Configuration.ORIENTATION_PORTRAIT ? szWindow.x - 50 : (szWindow.x - 150); + originalParams.height = orientation == Configuration.ORIENTATION_PORTRAIT ? (int) (szWindow.y / 1.8) : (szWindow.y - 200); + if (update) windowManager.updateViewLayout(drawerHolder.appDrawer, originalParams); + } + + @SuppressLint("InflateParams") + @Override public void onShow(@NonNull WindowManager windowManager, @NonNull View view, @NonNull FolderModel folder) { + this.windowManager = windowManager; + Context context = view.getContext(); + drawerHolder = new AppDrawerHolder(LayoutInflater.from(view.getContext()).inflate(R.layout.floating_folder_layout, null, false), this); + adapter = new FloatingAppsAdapter(new ArrayList(), getPresenter(), false); + drawerHolder.recycler.setAdapter(adapter); + drawerHolder.emptyText.setText(R.string.no_apps); + drawerHolder.recycler.setEmptyView(drawerHolder.emptyText); + NinePatchDrawable drawable = (NinePatchDrawable) drawerHolder.appDrawer.getBackground(); + drawable.setColorFilter(new PorterDuffColorFilter(folder.getColor(), + PorterDuff.Mode.MULTIPLY)); + setupParams(windowManager); + appsLoader = new SelectedAppsLoader(context, folder.getId()); + appsLoader.registerListener(folder.hashCode(), getPresenter()); + appsLoader.startLoading(); + } + + @Override public void onAppsLoaded(@Nullable List models) { + if (models != null) adapter.insertItems(models); + else adapter.clear(); + } + + @Override public void onConfigChanged(int orientation) { + updateParams(orientation, true); + } + + @Override public void onTouchedOutside() { + onDestroy(); + } + + @Override public void onBackPressed() { + onTouchedOutside(); + } + + @Override public void onDestroy() { + if (windowManager != null) { + if (drawerHolder != null && drawerHolder.appDrawer.isShown()) { + drawerHolder.appDrawer.animate().scaleY(0).scaleX(0).withEndAction(new Runnable() { + @Override public void run() { + windowManager.removeView(drawerHolder.appDrawer); + drawerHolder.onDestroy(); + if (appsLoader != null) appsLoader.unregisterListener(getPresenter()); + EventBus.getDefault().unregister(this); + getPresenter().onDestroy(); + } + }); + } + } + } + + @SuppressWarnings("unused") @Subscribe public void onEvent(ThemePackEventModel model) { + if (appsLoader != null) appsLoader.forceLoad(); + } + + private FloatingDrawPresenter getPresenter() { + if (presenter == null) { + presenter = FloatingDrawPresenter.with(this); + } + return presenter; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/MainMvp.java b/app/src/main/java/com/fastaccess/ui/modules/main/MainMvp.java new file mode 100644 index 0000000..ca6e163 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/main/MainMvp.java @@ -0,0 +1,87 @@ +package com.fastaccess.ui.modules.main; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.annotation.IntDef; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.NavigationView; +import android.support.design.widget.NavigationView.OnNavigationItemSelectedListener; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.widget.DrawerLayout; + +import com.fastaccess.ui.base.mvp.BaseMvp; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import it.sephiroth.android.library.bottomnavigation.BottomNavigation; +import it.sephiroth.android.library.bottomnavigation.BottomNavigation.OnMenuItemSelectionListener; + +/** + * Created by Kosh on 10 Oct 2016, 10:56 PM + */ + +public interface MainMvp { + + int DEVICE_APPS = 0; + int FOLDERS = 1; + int SELECTED_APPS = 2; + + @IntDef({ + DEVICE_APPS, + FOLDERS, + SELECTED_APPS, + }) + @Retention(RetentionPolicy.SOURCE) @interface NavigationType {} + + interface View { + void onNavigationChanged(@NavigationType int navType); + + void onOpenDrawer(); + + void onCloseDrawer(); + + void onOpenSettings(); + + void onStartService(); + + void onStopService(); + + void onShowBadge(@IdRes int itemId); + + void onHideBadge(@IdRes int itemId); + + void onSelectMenuItem(@IdRes int itemId); + + void onBackup(); + + void onRestore(); + } + + interface Presenter extends BaseMvp.FAPresenter, OnNavigationItemSelectedListener, OnMenuItemSelectionListener { + void onActivityStarted(@Nullable Bundle savedInstance, @NonNull MainView mainView, + @NonNull BottomNavigation bottomNavigation, + @NonNull NavigationView navigationView); + + boolean canBackPress(@NonNull DrawerLayout drawerLayout); + + void onModuleChanged(@NonNull FragmentManager fragmentManager, @NavigationType int type); + + void onShowHideFragment(@NonNull FragmentManager fragmentManager, @NonNull Fragment toShow, @NonNull Fragment toHide); + + void onAddAndHide(@NonNull FragmentManager fragmentManager, @NonNull Fragment toAdd, @NonNull Fragment toHide); + + void onFilterResult(@NonNull FragmentManager supportFragmentManager, @Nullable String text); + + void onCreateNewFolder(@NonNull FragmentManager supportFragmentManager); + + void onActivityForResult(int requestCode, int resultCode); + + void onHandleShortcuts(@NonNull MainView mainView, @Nullable Intent intent); + + void onBackupRestore(int backupType, @NonNull MainView mainView); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/MainPresenter.java b/app/src/main/java/com/fastaccess/ui/modules/main/MainPresenter.java new file mode 100644 index 0000000..10c9c34 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/main/MainPresenter.java @@ -0,0 +1,212 @@ +package com.fastaccess.ui.modules.main; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Typeface; +import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.NavigationView; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.view.MenuItem; + +import com.fastaccess.R; +import com.fastaccess.helper.Bundler; +import com.fastaccess.helper.Logger; +import com.fastaccess.helper.PermissionsHelper; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; +import com.fastaccess.ui.modules.apps.device.DeviceAppsView; +import com.fastaccess.ui.modules.apps.folders.FoldersView; +import com.fastaccess.ui.modules.apps.selected.SelectedAppsView; +import com.fastaccess.ui.modules.cloud.auth.LoginView; + +import it.sephiroth.android.library.bottomnavigation.BottomNavigation; + +import static com.fastaccess.helper.AppHelper.getFragmentByTag; +import static com.fastaccess.helper.AppHelper.getVisibleFragment; + +/** + * Created by Kosh on 10 Oct 2016, 11:13 PM + */ + +public class MainPresenter extends BasePresenter implements MainMvp.Presenter { + + public static MainPresenter with(MainMvp.View view) { + return new MainPresenter(view); + } + + protected MainPresenter(@NonNull MainMvp.View view) { + super(view); + } + + @Override public void onActivityStarted(@Nullable Bundle savedInstance, + @NonNull MainView mainView, + @NonNull BottomNavigation bottomNavigation, + @NonNull NavigationView navigationView) { + navigationView.setNavigationItemSelectedListener(this); + Typeface myTypeface = TypeFaceHelper.getTypeface(); + bottomNavigation.setDefaultTypeface(myTypeface); + bottomNavigation.setOnMenuItemClickListener(this); + if (savedInstance == null) { + bottomNavigation.setDefaultSelectedIndex(0); + } + } + + @Override public boolean canBackPress(@NonNull DrawerLayout drawerLayout) { + return !drawerLayout.isDrawerOpen(GravityCompat.START); + } + + @SuppressWarnings("ConstantConditions") + @Override public void onModuleChanged(@NonNull FragmentManager fragmentManager, @MainMvp.NavigationType int type) { + Logger.e(type); + Fragment currentVisible = getVisibleFragment(fragmentManager); + DeviceAppsView deviceAppsView = (DeviceAppsView) getFragmentByTag(fragmentManager, DeviceAppsView.TAG); + FoldersView foldersView = (FoldersView) getFragmentByTag(fragmentManager, FoldersView.TAG); + SelectedAppsView selectedAppsView = (SelectedAppsView) getFragmentByTag(fragmentManager, SelectedAppsView.TAG); + switch (type) { + case MainMvp.DEVICE_APPS: + if (deviceAppsView == null) { + onAddAndHide(fragmentManager, DeviceAppsView.newInstance(), currentVisible); + } else { + onShowHideFragment(fragmentManager, deviceAppsView, currentVisible); + } + break; + case MainMvp.FOLDERS: + if (foldersView == null) { + onAddAndHide(fragmentManager, FoldersView.newInstance(), currentVisible); + } else { + onShowHideFragment(fragmentManager, foldersView, currentVisible); + } + break; + case MainMvp.SELECTED_APPS: + if (selectedAppsView == null) { + onAddAndHide(fragmentManager, SelectedAppsView.newInstance(), currentVisible); + } else { + onShowHideFragment(fragmentManager, selectedAppsView, currentVisible); + } + break; + } + } + + @Override public void onShowHideFragment(@NonNull FragmentManager fragmentManager, @NonNull Fragment toShow, @NonNull Fragment toHide) { + Logger.e("show", toShow.getClass().getSimpleName(), "hide", toHide.getClass().getSimpleName()); + fragmentManager + .beginTransaction() + .hide(toHide) + .show(toShow) + .commit(); + } + + @Override public void onAddAndHide(@NonNull FragmentManager fragmentManager, @NonNull Fragment toAdd, @NonNull Fragment toHide) { + Logger.e("add", toAdd.getClass().getSimpleName(), "hide", toHide.getClass().getSimpleName()); + fragmentManager + .beginTransaction() + .hide(toHide) + .add(R.id.container, toAdd, toAdd.getClass().getSimpleName()) + .commit(); + } + + @Override public void onFilterResult(@NonNull FragmentManager supportFragmentManager, @Nullable String text) { + Fragment fragment = getVisibleFragment(supportFragmentManager); + Logger.e(fragment); + if (fragment != null) { + if (fragment instanceof DeviceAppsView) { + ((DeviceAppsView) fragment).onFilter(text); + } else if (fragment instanceof FoldersView) { + ((FoldersView) fragment).onFilter(text); + } else if (fragment instanceof SelectedAppsView) { + ((SelectedAppsView) fragment).onFilter(text); + } + } + } + + @Override public void onCreateNewFolder(@NonNull FragmentManager supportFragmentManager) { + Fragment foldersView = getVisibleFragment(supportFragmentManager); + if (!(foldersView instanceof FoldersView)) { + throw new RuntimeException("Oops, Folders Fragment is not currently visible."); + } + ((FoldersView) foldersView).onCreateNewFolder(); + } + + @Override public void onActivityForResult(int requestCode, int resultCode) { + if (requestCode == PermissionsHelper.OVERLAY_PERMISSION_REQ_CODE) { + if (isAttached() && resultCode == Activity.RESULT_OK) getView().onStartService(); + } + } + + @Override public void onHandleShortcuts(@NonNull MainView mainView, @Nullable Intent intent) { + if (intent != null && intent.getAction() != null) { + String action = intent.getAction(); + switch (action) { + case "FOLDER_MODE": + PrefHelper.set(PrefConstant.FLOATING_MODE, "Folders"); + if (isAttached()) { + getView().onStopService(); + getView().onStartService(); + } + break; + case "APPS_MODE": + PrefHelper.set(PrefConstant.FLOATING_MODE, "Apps"); + if (isAttached()) { + getView().onStopService(); + getView().onStartService(); + } + break; + } + } + } + + @Override public void onBackupRestore(int backupType, @NonNull MainView mainView) { + Intent intent = new Intent(mainView, LoginView.class); + intent.putExtras(Bundler.start().put(LoginView.TYPE, backupType).end()); + mainView.startActivity(intent); + } + + @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { + getView().onCloseDrawer(); + switch (item.getItemId()) { + case R.id.settings: + getView().onOpenSettings(); + return true; + case R.id.start: + getView().onStartService(); + return true; + case R.id.stop: + getView().onStopService(); + return true; + case R.id.myApps: + onMenuItemSelect(R.id.myApps, 0); + return true; + case R.id.folders: + onMenuItemSelect(R.id.folders, 1); + return true; + case R.id.selectedApps: + onMenuItemSelect(R.id.selectedApps, 2); + return true; + case R.id.backup: + getView().onBackup(); + break; + case R.id.restore: + getView().onRestore(); + break; + } + return false; + } + + @Override public void onMenuItemSelect(@IdRes int id, int position) { + if (isAttached()) { + getView().onNavigationChanged(position); + getView().onHideBadge(id); + getView().onSelectMenuItem(id); + } + } + + @Override public void onMenuItemReselect(@IdRes int id, int position) {} +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/main/MainView.java b/app/src/main/java/com/fastaccess/ui/modules/main/MainView.java new file mode 100644 index 0000000..e7bac7d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/main/MainView.java @@ -0,0 +1,227 @@ +package com.fastaccess.ui.modules.main; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.design.widget.AppBarLayout; +import android.support.design.widget.CoordinatorLayout; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.NavigationView; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.text.Editable; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; + +import com.fastaccess.R; +import com.fastaccess.helper.AnimHelper; +import com.fastaccess.helper.AppHelper; +import com.fastaccess.helper.NotificationHelper; +import com.fastaccess.helper.PermissionsHelper; +import com.fastaccess.provider.service.FloatingService; +import com.fastaccess.ui.base.BaseActivity; +import com.fastaccess.ui.modules.apps.device.DeviceAppsView; +import com.fastaccess.ui.modules.cloud.auth.LoginView; +import com.fastaccess.ui.modules.settings.SettingsView; +import com.fastaccess.ui.widgets.FontEditText; +import com.fastaccess.ui.widgets.ForegroundImageView; +import com.fastaccess.ui.widgets.dialog.MessageDialogView; + +import butterknife.BindView; +import butterknife.OnClick; +import butterknife.OnTextChanged; +import butterknife.OnTouch; +import icepick.State; +import it.sephiroth.android.library.bottomnavigation.BadgeProvider; +import it.sephiroth.android.library.bottomnavigation.BottomNavigation; + +public class MainView extends BaseActivity implements MainMvp.View { + public final static int BACKUP_REQUEST_CODE = 1; + public final static int RESTORE_REQUEST_CODE = 2; + + @MainMvp.NavigationType @State int navType; + + @BindView(R.id.searchEditText) FontEditText searchEditText; + @BindView(R.id.clear) ForegroundImageView clear; + @BindView(R.id.appbar) AppBarLayout appbar; + @BindView(R.id.coordinatorLayout) CoordinatorLayout coordinatorLayout; + @BindView(R.id.navigation) NavigationView navigation; + @BindView(R.id.drawerLayout) DrawerLayout drawerLayout; + @BindView(R.id.bottomNavigation) BottomNavigation bottomNavigation; + @BindView(R.id.fab) FloatingActionButton fab; + private MainPresenter presenter; + private BadgeProvider badgeProvider; + + + @OnClick(R.id.fab) void onClick() { + if (navType == MainMvp.FOLDERS) { + getPresenter().onCreateNewFolder(getSupportFragmentManager()); + } + } + + @OnTouch(R.id.searchEditText) boolean onTouch() { + appbar.setExpanded(false, true); + return false; + } + + @OnTextChanged(value = R.id.searchEditText, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED) void onTextChange(Editable s) { + String text = s.toString(); + if (text.length() == 0) { + getPresenter().onFilterResult(getSupportFragmentManager(), text); + AnimHelper.animateVisibility(clear, false); + } else { + AnimHelper.animateVisibility(clear, true); + getPresenter().onFilterResult(getSupportFragmentManager(), text); + } + } + + @OnClick(value = {R.id.searchIcon, R.id.clear}) void onClick(View view) { + if (view.getId() == R.id.clear) { + AppHelper.hideKeyboard(searchEditText); + searchEditText.setText(""); + } + } + + @Override protected int layout() { + return R.layout.activity_main; + } + + @NonNull @Override protected MainPresenter getPresenter() { + if (presenter == null) { + presenter = MainPresenter.with(this); + } + return presenter; + } + + @Override protected boolean isTransparent() { + return true; + } + + @Override protected boolean canBack() { + return false; + } + + @Override protected void onCreate(Bundle savedInstanceState) { + setTheme(R.style.AppTheme); + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.container, DeviceAppsView.newInstance(), DeviceAppsView.TAG) + .commit(); + getPresenter().onHandleShortcuts(this, getIntent()); + } + setToolbarIcon(R.drawable.ic_menu); + getPresenter().onActivityStarted(savedInstanceState, this, bottomNavigation, navigation); + if (null != savedInstanceState) getBadgeProvider().restore(savedInstanceState); + if (navType == MainMvp.FOLDERS) { + fab.show(); + } else { + fab.hide(); + } + } + + @Override public void onNavigationChanged(@MainMvp.NavigationType int navType) { + //noinspection WrongConstant + if (bottomNavigation.getSelectedIndex() != navType) bottomNavigation.setSelectedIndex(navType, true); + this.navType = navType; + getPresenter().onModuleChanged(getSupportFragmentManager(), navType); + if (navType == MainMvp.FOLDERS) { + fab.show(); + } else { + fab.hide(); + } + } + + @Override public void onOpenDrawer() { + if (!drawerLayout.isDrawerOpen(GravityCompat.START)) drawerLayout.openDrawer(GravityCompat.START); + } + + @Override public void onCloseDrawer() { + if (drawerLayout.isDrawerOpen(GravityCompat.START)) drawerLayout.closeDrawer(GravityCompat.START); + } + + @Override public void onOpenSettings() { + startActivity(new Intent(this, SettingsView.class)); + } + + @Override public void onStartService() { + if (PermissionsHelper.systemAlertPermissionIsGranted(this)) { + startService(new Intent(this, FloatingService.class)); + } else { + Toast.makeText(this, R.string.floating_window_warning, Toast.LENGTH_LONG).show(); + } + } + + @Override public void onStopService() { + stopService(new Intent(this, FloatingService.class)); + NotificationHelper.cancelAllNotifications(this); + } + + @Override public void onShowBadge(@IdRes int itemId) { + if (!getBadgeProvider().hasBadge(itemId)) { + getBadgeProvider().show(itemId); + } + } + + @Override public void onHideBadge(@IdRes int itemId) { + if (getBadgeProvider().hasBadge(itemId)) { + getBadgeProvider().remove(itemId); + } + } + + @Override public void onSelectMenuItem(@IdRes int itemId) { + navigation.getMenu().findItem(itemId).setChecked(true); + } + + @Override public void onBackup() { + MessageDialogView.newInstance(R.string.backup, R.string.backup_warning, BACKUP_REQUEST_CODE) + .show(getSupportFragmentManager(), "MessageDialogView"); + } + + @Override public void onRestore() { + MessageDialogView.newInstance(R.string.restore, R.string.restore_warning, RESTORE_REQUEST_CODE) + .show(getSupportFragmentManager(), "MessageDialogView"); + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onOpenDrawer(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override public void onBackPressed() { + if (getPresenter().canBackPress(drawerLayout)) { + super.onBackPressed(); + } else { + drawerLayout.closeDrawer(GravityCompat.START); + } + } + + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + getPresenter().onActivityForResult(requestCode, resultCode); + } + + @Override protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + getPresenter().onHandleShortcuts(this, intent); + } + + @NonNull private BadgeProvider getBadgeProvider() { + if (badgeProvider == null) { + badgeProvider = bottomNavigation.getBadgeProvider(); + } + return badgeProvider; + } + + @Override public void onMessageDialogActionClicked(boolean isOk, int requestCode) { + if (isOk) { + getPresenter().onBackupRestore(requestCode == BACKUP_REQUEST_CODE ? LoginView.BACKUP_TYPE : LoginView.RESTORE_TYPE, this); + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsFragmentView.java b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsFragmentView.java new file mode 100644 index 0000000..a344c17 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsFragmentView.java @@ -0,0 +1,239 @@ +package com.fastaccess.ui.modules.settings; + +import android.Manifest; +import android.app.Activity; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.v14.preference.SwitchPreference; +import android.support.v4.app.ActivityCompat; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.PreferenceGroupAdapter; +import android.view.View; +import android.widget.Toast; + +import com.fastaccess.App; +import com.fastaccess.BuildConfig; +import com.fastaccess.R; +import com.fastaccess.data.dao.FloatingEventModel; +import com.fastaccess.helper.ActivityHelper; +import com.fastaccess.helper.AppHelper; +import com.fastaccess.helper.FileHelper; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.helper.Logger; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.provider.icon.IconPackHelper; +import com.fastaccess.ui.modules.settings.dialogs.CustomIconChooserDialog; +import com.fastaccess.ui.modules.settings.dialogs.IconSizeTransparencyDialog; +import com.google.common.io.Files; +import com.theartofdev.edmodo.cropper.CropImage; +import com.theartofdev.edmodo.cropper.CropImageView; + +import org.greenrobot.eventbus.EventBus; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import pub.devrel.easypermissions.AfterPermissionGranted; +import pub.devrel.easypermissions.EasyPermissions; + +import static android.app.Activity.RESULT_OK; + +/** + * Created by Kosh on 15 Oct 2016, 10:49 PM + */ + +public class SettingsFragmentView extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener, SharedPreferences + .OnSharedPreferenceChangeListener, CustomIconChooserDialog.OnCustomIconChooseCallback, EasyPermissions.PermissionCallbacks { + + private Toast toast; + + private final static String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}; + + private void showToast(@StringRes int resId) { + if (toast != null) toast.cancel(); + toast = Toast.makeText(App.getInstance(), resId, Toast.LENGTH_LONG);//getContext() might be null when onSharedPreferenceChanged, weird + // behavior + toast.show(); + } + + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.fa_settings); + } + + @Override public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + setDivider(ActivityCompat.getDrawable(getActivity(), R.drawable.list_divider)); + setDividerHeight(1); + PreferenceGroupAdapter adapter = (PreferenceGroupAdapter) getListView().getAdapter(); + for (int i = 0; i < getListView().getAdapter().getItemCount(); i++) {//lazy global setOnPreferenceClickListener + Preference preference = adapter.getItem(i); + if (preference != null && !InputHelper.isEmpty(preference.getKey())) { + if (preference.getKey().equalsIgnoreCase("version")) { + preference.setSummary(BuildConfig.VERSION_NAME); + } else if (!(preference instanceof SwitchPreference) && !(preference instanceof ListPreference)) { + preference.setOnPreferenceClickListener(this); + } + } + } + } + + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK) { + if (requestCode == IconPackHelper.PICK_ICON) { + Bitmap bitmap = data.getParcelableExtra("icon"); + if (bitmap == null) { + Uri uri = data.getData(); + if (uri != null) { + File file = FileHelper.generateFile("fa_image_icon"); + try { + Files.copy(new File(uri.getPath()), file); + } catch (IOException e) { + e.printStackTrace(); + showToast(R.string.error_retrieving_icon); + return; + } + PrefHelper.set(PrefConstant.CUSTOM_ICON, file.getPath()); + EventBus.getDefault().post(new FloatingEventModel(true, PrefConstant.CUSTOM_ICON)); + } else { + showToast(R.string.error_retrieving_icon); + } + } else { + String path = AppHelper.saveBitmap(bitmap); + if (path == null) { + showToast(R.string.write_sdcard_explanation); + return; + } + PrefHelper.set(PrefConstant.CUSTOM_ICON, path); + EventBus.getDefault().post(new FloatingEventModel(true, PrefConstant.CUSTOM_ICON)); + if (!bitmap.isRecycled()) { + bitmap.recycle(); + } + } + } else if (requestCode == ActivityHelper.SELECT_PHOTO_REQUEST) { + CropImage.activity(data.getData()) + .setCropShape(CropImageView.CropShape.OVAL) + .setOutputUri(Uri.fromFile(FileHelper.generateFile("fa_image_icon"))) + .setAspectRatio(5, 5) + .setFixAspectRatio(true) + .setOutputCompressFormat(Bitmap.CompressFormat.PNG) + .start(getContext(), this); + } else if (requestCode == CropImage.CROP_IMAGE_ACTIVITY_REQUEST_CODE) { + CropImage.ActivityResult result = CropImage.getActivityResult(data); + Uri resultUri = result.getUri(); + PrefHelper.set(PrefConstant.CUSTOM_ICON, new File(resultUri.getPath()).getPath()); + EventBus.getDefault().post(new FloatingEventModel(true, PrefConstant.CUSTOM_ICON)); + } + } else if (resultCode == Activity.RESULT_CANCELED) { + if (requestCode == IconPackHelper.PICK_ICON) { + if (data != null) { + boolean defaultIcon = data.getBooleanExtra("default", false); + if (defaultIcon) { + PrefHelper.set(PrefConstant.CUSTOM_ICON, ""); + EventBus.getDefault().post(new FloatingEventModel(true, PrefConstant.CUSTOM_ICON)); + } + } + } + } + + } + + @Override public boolean onPreferenceClick(Preference preference) { + switch (preference.getKey()) { + case "fa_background_alpha": + IconSizeTransparencyDialog.newInstance(true).show(getChildFragmentManager(), "IconSizeTransparencyDialog"); + return true; + case "icon_pack": + IconPackHelper.pickIconPack(this, false); + return true; + case "custom_icon": + new CustomIconChooserDialog().show(getChildFragmentManager(), "CustomIconChooserDialog"); + return true; + case "icon_alpha": + IconSizeTransparencyDialog.newInstance(false).show(getChildFragmentManager(), "IconSizeTransparencyDialog"); + return true; + case "manual_size": + IconSizeTransparencyDialog.newInstance(false, true).show(getChildFragmentManager(), "IconSizeTransparencyDialog"); + return true; + case "version": + return true; + case "sourceCode": + ActivityHelper.startCustomTab(getActivity()); + return true; + case "libraries": + ActivityHelper.startLibs(getActivity()); + return true; + } + return false; + } + + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if (key == null) return; + if (key.equalsIgnoreCase(PrefConstant.STATUS_BAR_HIDDEN) || key.equalsIgnoreCase(PrefConstant.FA_IS_HORIZONTAL)) { + showToast(R.string.required_restart); + } else if (key.equalsIgnoreCase(PrefConstant.ICON_SIZE)) { + sharedPreferences.edit().putInt(PrefConstant.MANUAL_SIZE, 0).apply(); + } + EventBus.getDefault().post(new FloatingEventModel(true, key)); + } + + @Override public void onResume() { + super.onResume(); + PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); + } + + @Override public void onPause() { + super.onPause(); + PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); + } + + @Override public void onUserChoose(boolean isFromGallery) { + if (!isFromGallery) { + pickIcon(); + } else { + pickImage(); + } + } + + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); + } + + @Override public void onPermissionsGranted(int requestCode, List perms) { + Logger.e(); + } + + @Override public void onPermissionsDenied(int requestCode, List perms) { + Logger.e(); + } + + @AfterPermissionGranted(IconPackHelper.PICK_ICON) private void pickIcon() { + if (EasyPermissions.hasPermissions(getContext(), permissions)) { + IconPackHelper.pickIconPack(this, true); + } else { + EasyPermissions.requestPermissions(this, getString(R.string.write_sdcard_explanation), + IconPackHelper.PICK_ICON, permissions); + } + } + + @AfterPermissionGranted(ActivityHelper.SELECT_PHOTO_REQUEST) private void pickImage() { + if (EasyPermissions.hasPermissions(getContext(), permissions)) { + ActivityHelper.startGalleryIntent(this); + } else { + EasyPermissions.requestPermissions(this, getString(R.string.write_sdcard_explanation), + ActivityHelper.SELECT_PHOTO_REQUEST, permissions); + } + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsView.java b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsView.java new file mode 100644 index 0000000..915023a --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/SettingsView.java @@ -0,0 +1,30 @@ +package com.fastaccess.ui.modules.settings; + +import android.support.annotation.NonNull; + +import com.fastaccess.R; +import com.fastaccess.ui.base.BaseActivity; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +/** + * Created by Kosh on 15 Oct 2016, 10:52 PM + */ + +public class SettingsView extends BaseActivity { + + @Override protected int layout() { + return R.layout.settings_layout; + } + + @NonNull @Override protected BasePresenter getPresenter() { + return null; + }//op-out + + @Override protected boolean isTransparent() { + return false; + } + + @Override protected boolean canBack() { + return true; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/CropImageActivityDialog.java b/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/CropImageActivityDialog.java new file mode 100644 index 0000000..addb4c9 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/CropImageActivityDialog.java @@ -0,0 +1,72 @@ +package com.fastaccess.ui.modules.settings.dialogs; + +import android.support.annotation.NonNull; + +import com.fastaccess.R; +import com.fastaccess.ui.base.BaseActivity; +import com.fastaccess.ui.base.mvp.presenter.BasePresenter; + +/** + * Created by Kosh on 20 Oct 2016, 8:29 PM + */ + +public class CropImageActivityDialog extends BaseActivity { +// +// @BindView(R.id.done) ForegroundImageView done; +// @BindView(R.id.cutterImageView) CookieCutterImageView cutterImageView; +// @State Uri uri; +// +// @OnClick(R.id.done) void onDone() { +// Bitmap bitmap = cutterImageView.getCroppedBitmap(); +// if (bitmap != null) { +// Bitmap circularBitmap = ImageUtils.getCircularBitmap(bitmap); +// String path = AppHelper.saveBitmap(circularBitmap); +// if (path == null) { +// Toast.makeText(this, R.string.write_sdcard_explanation, Toast.LENGTH_SHORT).show(); +// return; +// } +// PrefHelper.set(PrefConstant.CUSTOM_ICON, path); +// EventBus.getDefault().post(new FloatingEventModel(true, PrefConstant.CUSTOM_ICON)); +// if (!bitmap.isRecycled() && !circularBitmap.isRecycled()) { +// bitmap.recycle(); +// circularBitmap.recycle(); +// } +// } +// finish(); +// } + + @Override protected int layout() { + return R.layout.crop_image_layout; + } + + @NonNull @Override protected BasePresenter getPresenter() { + return null; + } + + @Override protected boolean isTransparent() { + return false; + } + + @Override protected boolean canBack() { + return true; + } + +// @Override public void onCreate(@Nullable Bundle savedInstanceState) { +// super.onCreate(savedInstanceState); +// if (savedInstanceState == null) { +// uri = getIntent().getExtras().getParcelable("uri"); +// } +// if (uri == null) { +// finish(); +// return; +// } +// Bitmap bitmap = AppHelper.getBitmapFromUri(uri, this); +// if (bitmap != null) { +// cutterImageView.getParams().setShape(CookieCutterShape.CIRCLE); +// cutterImageView.invalidate(); +// cutterImageView.setImageBitmap(bitmap); +// } else { +// finish(); +// } +// } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/CustomIconChooserDialog.java b/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/CustomIconChooserDialog.java new file mode 100644 index 0000000..6d813c5 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/CustomIconChooserDialog.java @@ -0,0 +1,54 @@ +package com.fastaccess.ui.modules.settings.dialogs; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.view.View; +import android.widget.FrameLayout; + +import com.fastaccess.R; +import com.fastaccess.ui.base.BaseBottomSheetDialog; + +import butterknife.BindView; +import butterknife.OnClick; + +/** + * Created by Kosh on 19 Oct 2016, 8:08 PM + */ + +public class CustomIconChooserDialog extends BaseBottomSheetDialog { + + @BindView(R.id.fromIconPack) FrameLayout fromIconPack; + @BindView(R.id.fromGallery) FrameLayout fromGallery; + + public interface OnCustomIconChooseCallback { + void onUserChoose(boolean isFromGallery); + } + + private OnCustomIconChooseCallback callback; + + @OnClick(value = {R.id.fromIconPack, R.id.fromGallery}) void onClick(View view) { + callback.onUserChoose(view.getId() == R.id.fromGallery); + dismiss(); + } + + @Override public void onAttach(Context context) { + super.onAttach(context); + if (!(getParentFragment() instanceof OnCustomIconChooseCallback)) { + throw new RuntimeException("Parent Fragment must implement OnCustomIconChooseCallback"); + } + callback = (OnCustomIconChooseCallback) getParentFragment(); + } + + @Override public void onDetach() { + super.onDetach(); + callback = null; + } + + @Override protected int layoutRes() { + return R.layout.icon_chooser_layout; + } + + @Override protected void onViewCreated(@NonNull View view) { + + } +} diff --git a/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/IconSizeTransparencyDialog.java b/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/IconSizeTransparencyDialog.java new file mode 100644 index 0000000..c32117f --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/modules/settings/dialogs/IconSizeTransparencyDialog.java @@ -0,0 +1,105 @@ +package com.fastaccess.ui.modules.settings.dialogs; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.Toolbar; +import android.view.View; + +import com.fastaccess.R; +import com.fastaccess.helper.Bundler; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.fastaccess.helper.ViewHelper; +import com.fastaccess.ui.base.BaseBottomSheetDialog; +import com.fastaccess.ui.widgets.FontTextView; +import com.fastaccess.ui.widgets.ForegroundImageView; + +import org.adw.library.widgets.discreteseekbar.DiscreteSeekBar; + +import butterknife.BindView; +import butterknife.OnClick; + +/** + * Created by Kosh on 16 Oct 2016, 6:46 PM + */ + +public class IconSizeTransparencyDialog extends BaseBottomSheetDialog implements DiscreteSeekBar.OnProgressChangeListener { + + @BindView(R.id.done) ForegroundImageView done; + @BindView(R.id.valueText) FontTextView valueText; + @BindView(R.id.seekBar) DiscreteSeekBar seekBar; + @BindView(R.id.toolbar) Toolbar toolbar; + private boolean isBackground; + private boolean isSize; + + public static IconSizeTransparencyDialog newInstance(boolean isBackground) { + IconSizeTransparencyDialog dialog = new IconSizeTransparencyDialog(); + dialog.setArguments(Bundler.start().put("isBackground", isBackground).end()); + return dialog; + } + + public static IconSizeTransparencyDialog newInstance(boolean isBackground, boolean isSize) { + IconSizeTransparencyDialog dialog = new IconSizeTransparencyDialog(); + dialog.setArguments(Bundler.start().put("isBackground", isBackground).put("isSize", isSize).end()); + return dialog; + } + + @OnClick(R.id.done) void onDone() { + if (!isSize) { + PrefHelper.set(isBackground ? PrefConstant.FA_BACKGROUND_ALPHA : PrefConstant.ICON_ALPHA, seekBar.getProgress()); + } else { + PrefHelper.set(PrefConstant.MANUAL_SIZE, seekBar.getProgress()); + } + dismiss(); + } + + @Override protected int layoutRes() { + return R.layout.transparency_layout; + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + isSize = getArguments().getBoolean("isSize"); + isBackground = getArguments().getBoolean("isBackground"); + } + + @Override protected void onViewCreated(@NonNull View view) { + if (isSize) toolbar.setTitle(R.string.change_size); + seekBar.setOnProgressChangeListener(this); + if (isSize) { + seekBar.setMin(100); + seekBar.setMax(300); + seekBar.setProgress(PrefHelper.getInt(PrefConstant.MANUAL_SIZE)); + } else { + seekBar.setProgress(PrefHelper.getInt(isBackground ? PrefConstant.FA_BACKGROUND_ALPHA : PrefConstant.ICON_ALPHA)); + } + } + + @Override public void onProgressChanged(DiscreteSeekBar seekBar, int value, boolean fromUser) { + changeIconAlpha(value); + } + + private void changeIconAlpha(int value) { + valueText.setText(String.format("%s: %s", getString(R.string.value), value)); + Drawable drawable = valueText.getCompoundDrawables()[2];//end drawable + if (!isSize) { + if (drawable != null) drawable.setAlpha(value); + } else { + if (drawable != null) { + int height = ViewHelper.toPx(getContext(), value); + drawable.setBounds(0, 0, height, height); + valueText.getLayoutParams().height = height; + } + } + } + + @Override public void onStartTrackingTouch(DiscreteSeekBar seekBar) { + + } + + @Override public void onStopTrackingTouch(DiscreteSeekBar seekBar) { + + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/AppbarRefreshLayout.java b/app/src/main/java/com/fastaccess/ui/widgets/AppbarRefreshLayout.java new file mode 100644 index 0000000..962432f --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/AppbarRefreshLayout.java @@ -0,0 +1,56 @@ +package com.fastaccess.ui.widgets; + +import android.app.Activity; +import android.content.Context; +import android.support.design.widget.AppBarLayout; +import android.support.v4.widget.SwipeRefreshLayout; +import android.util.AttributeSet; + +import com.fastaccess.R; + + +/** + * Created by kosh on 7/30/2015. CopyRights @ Innov8tif + */ +public class AppbarRefreshLayout extends SwipeRefreshLayout implements AppBarLayout.OnOffsetChangedListener { + private AppBarLayout appBarLayout; + private boolean isReallyDisabled = false; + + public AppbarRefreshLayout(Context context) { + super(context, null); + } + + public AppbarRefreshLayout(Context context, AttributeSet attrs) { + super(context, attrs); + setColorSchemeResources(R.color.primary, R.color.primary_dark, R.color.primary_light, R.color.accent); + } + + public void setReallyDisable() { + this.setEnabled(false); + isReallyDisabled = true; + } + + @Override protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (getContext() instanceof Activity) { + appBarLayout = (AppBarLayout) ((Activity) getContext()).findViewById(R.id.appbar); + if (appBarLayout != null) { + appBarLayout.addOnOffsetChangedListener(this); + } + } + } + + @Override protected void onDetachedFromWindow() { + if (appBarLayout != null) { + appBarLayout.removeOnOffsetChangedListener(this); + appBarLayout = null; + } + super.onDetachedFromWindow(); + } + + @Override public void onOffsetChanged(AppBarLayout appBarLayout, int i) { + if (!isReallyDisabled) { + this.setEnabled(i == 0); + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FastBitmapDrawable.java b/app/src/main/java/com/fastaccess/ui/widgets/FastBitmapDrawable.java new file mode 100644 index 0000000..cea095f --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FastBitmapDrawable.java @@ -0,0 +1,221 @@ +package com.fastaccess.ui.widgets; + +import android.animation.ObjectAnimator; +import android.animation.TimeInterpolator; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.util.SparseArray; + +public class FastBitmapDrawable extends Drawable { + + private static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() { + @Override public float getInterpolation(float input) { + if (input < 0.05f) { + return input / 0.05f; + } else if (input < 0.3f) { + return 1; + } else { + return (1 - input) / 0.7f; + } + } + }; + private static final long CLICK_FEEDBACK_DURATION = 500; + private static final int PRESSED_BRIGHTNESS = 100; + private static ColorMatrix sGhostModeMatrix; + private static final ColorMatrix sTempMatrix = new ColorMatrix(); + /** + * Store the brightness colors filters to optimize animations during icon press. This only works for non-ghost-mode icons. + */ + private static final SparseArray sCachedBrightnessFilter = new SparseArray<>(); + + private static final int GHOST_MODE_MIN_COLOR_RANGE = 130; + + private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); + private final Bitmap mBitmap; + private int mAlpha; + + private int mBrightness = 0; + private boolean mGhostModeEnabled = false; + + private boolean mPressed = false; + private ObjectAnimator mPressedAnimator; + + public FastBitmapDrawable(Bitmap b) { + mAlpha = 255; + mBitmap = b; + setBounds(0, 0, b.getWidth(), b.getHeight()); + } + + @Override + public void draw(@NonNull Canvas canvas) { + final Rect r = getBounds(); + // Draw the bitmap into the bounding rect + canvas.drawBitmap(mBitmap, null, r, mPaint); + } + + @Override + public void setColorFilter(ColorFilter cf) { + // No op + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + mAlpha = alpha; + mPaint.setAlpha(alpha); + } + + @Override + public void setFilterBitmap(boolean filterBitmap) { + mPaint.setFilterBitmap(filterBitmap); + mPaint.setAntiAlias(filterBitmap); + } + + public int getAlpha() { + return mAlpha; + } + + @Override + public int getIntrinsicWidth() { + int width = getBounds().width(); + if (width == 0) { + width = mBitmap.getWidth(); + } + return width; + } + + @Override + public int getIntrinsicHeight() { + int height = getBounds().height(); + if (height == 0) { + height = mBitmap.getHeight(); + } + return height; + } + + @Override + public int getMinimumWidth() { + return getBounds().width(); + } + + @Override + public int getMinimumHeight() { + return getBounds().height(); + } + + public Bitmap getBitmap() { + return mBitmap; + } + + /** + * When enabled, the icon is grayed out and the contrast is increased to give it a 'ghost' appearance. + */ + public void setGhostModeEnabled(boolean enabled) { + if (mGhostModeEnabled != enabled) { + mGhostModeEnabled = enabled; + updateFilter(); + } + } + + public void setPressed(boolean pressed) { + if (mPressed != pressed) { + mPressed = pressed; + if (mPressed) { + mPressedAnimator = ObjectAnimator + .ofInt(this, "brightness", PRESSED_BRIGHTNESS) + .setDuration(CLICK_FEEDBACK_DURATION); + mPressedAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR); + mPressedAnimator.start(); + } else if (mPressedAnimator != null) { + mPressedAnimator.cancel(); + setBrightness(0); + } + } + invalidateSelf(); + } + + public boolean isPressed() { + return mPressed; + } + + public boolean isGhostModeEnabled() { + return mGhostModeEnabled; + } + + public int getBrightness() { + return mBrightness; + } + + public void setBrightness(int brightness) { + if (mBrightness != brightness) { + mBrightness = brightness; + updateFilter(); + invalidateSelf(); + } + } + + private void updateFilter() { + if (mGhostModeEnabled) { + if (sGhostModeMatrix == null) { + sGhostModeMatrix = new ColorMatrix(); + sGhostModeMatrix.setSaturation(0); + + // For ghost mode, set the color range to [GHOST_MODE_MIN_COLOR_RANGE, 255] + float range = (255 - GHOST_MODE_MIN_COLOR_RANGE) / 255.0f; + sTempMatrix.set(new float[]{ + range, 0, 0, 0, GHOST_MODE_MIN_COLOR_RANGE, + 0, range, 0, 0, GHOST_MODE_MIN_COLOR_RANGE, + 0, 0, range, 0, GHOST_MODE_MIN_COLOR_RANGE, + 0, 0, 0, 1, 0}); + sGhostModeMatrix.preConcat(sTempMatrix); + } + + if (mBrightness == 0) { + mPaint.setColorFilter(new ColorMatrixColorFilter(sGhostModeMatrix)); + } else { + setBrightnessMatrix(sTempMatrix, mBrightness); + sTempMatrix.postConcat(sGhostModeMatrix); + mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix)); + } + } else if (mBrightness != 0) { + ColorFilter filter = sCachedBrightnessFilter.get(mBrightness); + if (filter == null) { + filter = new PorterDuffColorFilter(Color.argb(mBrightness, 255, 255, 255), + PorterDuff.Mode.SRC_ATOP); + sCachedBrightnessFilter.put(mBrightness, filter); + } + mPaint.setColorFilter(filter); + } else { + mPaint.setColorFilter(null); + } + } + + private static void setBrightnessMatrix(ColorMatrix matrix, int brightness) { + // Brightness: C-new = C-old*(1-amount) + amount + float scale = 1 - brightness / 255.0f; + matrix.setScale(scale, scale, scale, 1); + float[] array = matrix.getArray(); + + // Add the amount to RGB components of the matrix, as per the above formula. + // Fifth elements in the array correspond to the constant being added to + // red, blue, green, and alpha channel respectively. + array[4] = brightness; + array[9] = brightness; + array[14] = brightness; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FitWidthImageView.java b/app/src/main/java/com/fastaccess/ui/widgets/FitWidthImageView.java new file mode 100644 index 0000000..8d5092b --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FitWidthImageView.java @@ -0,0 +1,60 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.widget.ImageView; + +/** + * Created by kosh on 12/7/2014. CopyRights @ innov8tif.com + */ +public class FitWidthImageView extends ImageView { + + /** + * Instantiates a new Fit width image. + * + * @param paramContext + * the param context + */ + public FitWidthImageView(Context paramContext) { + super(paramContext); + + } + + /** + * Instantiates a new Fit width image. + * + * @param paramContext + * the param context + * @param paramAttributeSet + * the param attribute set + */ + public FitWidthImageView(Context paramContext, AttributeSet paramAttributeSet) { + super(paramContext, paramAttributeSet); + } + + /** + * Instantiates a new Fit width image. + * + * @param paramContext + * the param context + * @param paramAttributeSet + * the param attribute set + * @param paramInt + * the param int + */ + public FitWidthImageView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) { + super(paramContext, paramAttributeSet, paramInt); + } + + @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { + final Drawable d = this.getDrawable(); + if (d != null) { + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = width * d.getIntrinsicHeight() / d.getIntrinsicWidth(); + setMeasuredDimension(width, height); + } else { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontAutoCompleteEditText.java b/app/src/main/java/com/fastaccess/ui/widgets/FontAutoCompleteEditText.java new file mode 100644 index 0000000..04da4d1 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontAutoCompleteEditText.java @@ -0,0 +1,48 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatAutoCompleteTextView; +import android.util.AttributeSet; +import android.widget.AdapterView; + +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.helper.ViewHelper; + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class FontAutoCompleteEditText extends AppCompatAutoCompleteTextView { + + public FontAutoCompleteEditText(Context context) { + super(context); + init(); + } + + public FontAutoCompleteEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + + } + + public FontAutoCompleteEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + TypeFaceHelper.applyTypeface(this); + } + + public void setTextColor(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setTextColor(ViewHelper.textSelector(nColor, pColor)); + } + + @Override public void setOnItemClickListener(AdapterView.OnItemClickListener l) { + super.setOnItemClickListener(l); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontButton.java b/app/src/main/java/com/fastaccess/ui/widgets/FontButton.java new file mode 100644 index 0000000..da926d6 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontButton.java @@ -0,0 +1,50 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatButton; +import android.util.AttributeSet; + +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.helper.ViewHelper; + + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class FontButton extends AppCompatButton { + + public FontButton(Context context) { + super(context); + init(); + } + + public FontButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public FontButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + TypeFaceHelper.applyTypeface(this); + } + + public void setBackground(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setBackgroundDrawable(ViewHelper.getDrawableSelector(nColor, pColor)); + } + + public void setTextColor(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setTextColor(ViewHelper.textSelector(nColor, pColor)); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontCheckbox.java b/app/src/main/java/com/fastaccess/ui/widgets/FontCheckbox.java new file mode 100644 index 0000000..abe0560 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontCheckbox.java @@ -0,0 +1,43 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatCheckBox; +import android.util.AttributeSet; + +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.helper.ViewHelper; + + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class FontCheckbox extends AppCompatCheckBox { + + public FontCheckbox(Context context) { + super(context); + init(); + } + + public FontCheckbox(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public FontCheckbox(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + TypeFaceHelper.applyTypeface(this); + } + + public void setTextColor(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setTextColor(ViewHelper.textSelector(nColor, pColor)); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontEditText.java b/app/src/main/java/com/fastaccess/ui/widgets/FontEditText.java new file mode 100644 index 0000000..c53a0ad --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontEditText.java @@ -0,0 +1,46 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatEditText; +import android.util.AttributeSet; +import android.view.inputmethod.EditorInfo; + +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.helper.ViewHelper; + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class FontEditText extends AppCompatEditText { + + public FontEditText(Context context) { + super(context); + init(); + } + + public FontEditText(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + + } + + public FontEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + setInputType(getInputType() | EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN); + setImeOptions(getImeOptions() | EditorInfo.IME_FLAG_NO_FULLSCREEN); + TypeFaceHelper.applyTypeface(this); + } + + public void setTextColor(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setTextColor(ViewHelper.textSelector(nColor, pColor)); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontFitTextView.java b/app/src/main/java/com/fastaccess/ui/widgets/FontFitTextView.java new file mode 100644 index 0000000..0a8d05c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontFitTextView.java @@ -0,0 +1,86 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.DisplayMetrics; + +/** + * Created by Kosh on 29 Apr 2016, 5:47 PM + */ +public class FontFitTextView extends FontTextView { + private Paint paint; + + public FontFitTextView(Context var1) { + super(var1); + this.init(); + } + + public FontFitTextView(Context var1, AttributeSet var2) { + super(var1, var2); + this.init(); + } + + @Override protected void onMeasure(int var1, int var2) { + super.onMeasure(var1, var2); + var1 = MeasureSpec.getSize(var1); + var2 = this.getMeasuredHeight(); + this.resize(this.getText().toString(), var1); + this.setMeasuredDimension(var1, var2); + } + + @Override protected void onTextChanged(CharSequence var1, int var2, int var3, int var4) { + this.resize(var1.toString(), this.getWidth()); + } + + @Override protected void onSizeChanged(int var1, int var2, int var3, int var4) { + if (var1 != var3) { + this.resize(this.getText().toString(), var1); + } + + } + + private void init() { + this.paint = new Paint(); + this.paint.set(this.getPaint()); + } + + private void resize(String var1, int var2) { + if (var2 > 0) { + var2 = var2 - this.getPaddingLeft() - this.getPaddingRight(); + DisplayMetrics var3 = this.getResources().getDisplayMetrics(); + float var4 = 32.0F * var3.density; + float var11 = 6.0F * var3.density; + this.paint.set(this.getPaint()); + + while (var4 - var11 > 0.5F) { + float var5 = (var4 + var11) / 2.0F; + this.paint.setTextSize(var5); + FontFitTextView var6 = this; + float var7; + String[] var8; + if ((var8 = var1.split("\n")) != null && var8.length != 0) { + var7 = this.paint.measureText(var8[0]); + + for (int var9 = 1; var9 < var8.length; ++var9) { + float var10; + if ((var10 = var6.paint.measureText(var8[var9])) > var7) { + var7 = var10; + } + } + } else { + var7 = this.paint.measureText(var1); + } + + if (var7 >= (float) var2) { + var4 = var5; + } else { + var11 = var5; + } + } + + this.setTextSize(0, var11); + } + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontRadioButton.java b/app/src/main/java/com/fastaccess/ui/widgets/FontRadioButton.java new file mode 100644 index 0000000..2295227 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontRadioButton.java @@ -0,0 +1,43 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatRadioButton; +import android.util.AttributeSet; + +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.helper.ViewHelper; + + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class FontRadioButton extends AppCompatRadioButton { + + public FontRadioButton(Context context) { + super(context); + init(); + } + + public FontRadioButton(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public FontRadioButton(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + TypeFaceHelper.applyTypeface(this); + } + + public void setTextColor(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setTextColor(ViewHelper.textSelector(nColor, pColor)); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/FontTextView.java b/app/src/main/java/com/fastaccess/ui/widgets/FontTextView.java new file mode 100644 index 0000000..fd0fe59 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/FontTextView.java @@ -0,0 +1,45 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.annotation.ColorRes; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatTextView; +import android.util.AttributeSet; + +import com.fastaccess.helper.TypeFaceHelper; +import com.fastaccess.helper.ViewHelper; + + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class FontTextView extends AppCompatTextView { + + public FontTextView(Context context) { + super(context); + init(); + } + + public FontTextView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + + } + + public FontTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + TypeFaceHelper.applyTypeface(this); + } + + public void setTextColor(@ColorRes int normalColor, @ColorRes int pressedColor) { + int nColor = ContextCompat.getColor(getContext(), normalColor); + int pColor = ContextCompat.getColor(getContext(), pressedColor); + setTextColor(ViewHelper.textSelector(nColor, pColor)); + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/ForegroundImageView.java b/app/src/main/java/com/fastaccess/ui/widgets/ForegroundImageView.java new file mode 100644 index 0000000..5021b7a --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/ForegroundImageView.java @@ -0,0 +1,115 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatImageView; +import android.util.AttributeSet; + +import com.fastaccess.R; + + +/** + * Created by Kosh on 22/11/15 2:29 PM. Fast Access + */ +public class ForegroundImageView extends AppCompatImageView { + private Drawable foreground; + + public ForegroundImageView(Context context) { + this(context, null); + } + + public ForegroundImageView(Context context, AttributeSet attrs) { + super(context, attrs, 0); + } + + public ForegroundImageView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundImageView); + Drawable foreground = a.getDrawable(R.styleable.ForegroundImageView_android_foreground); + if (foreground != null) { + setForeground(foreground); + } + a.recycle(); + } + + /** + * Supply a drawable resource that is to be rendered on top of all of the child views in the frame layout. + * + * @param drawableResId + * The drawable resource to be drawn on top of the children. + */ + public void setForegroundResource(int drawableResId) { + if (isInEditMode()) return; + setForeground(ContextCompat.getDrawable(getContext(), drawableResId)); + } + + /** + * Supply a Drawable that is to be rendered on top of all of the child views in the frame layout. + * + * @param drawable + * The Drawable to be drawn on top of the children. + */ + public void setForeground(Drawable drawable) { + if (foreground == drawable) { + return; + } + if (foreground != null) { + foreground.setCallback(null); + unscheduleDrawable(foreground); + } + + foreground = drawable; + + if (drawable != null) { + drawable.setCallback(this); + if (drawable.isStateful()) { + drawable.setState(getDrawableState()); + } + } + requestLayout(); + invalidate(); + } + + @Override protected boolean verifyDrawable(Drawable who) { + return super.verifyDrawable(who) || who == foreground; + } + + @Override public void jumpDrawablesToCurrentState() { + super.jumpDrawablesToCurrentState(); + if (foreground != null) foreground.jumpToCurrentState(); + } + + @Override protected void drawableStateChanged() { + super.drawableStateChanged(); + if (foreground != null && foreground.isStateful()) { + foreground.setState(getDrawableState()); + } + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (foreground != null) { + foreground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); + invalidate(); + } + } + + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + if (foreground != null) { + foreground.setBounds(0, 0, w, h); + invalidate(); + } + } + + @Override public void draw(Canvas canvas) { + super.draw(canvas); + + if (foreground != null) { + foreground.draw(canvas); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/widgets/NestedCoordinatorLayout.java b/app/src/main/java/com/fastaccess/ui/widgets/NestedCoordinatorLayout.java new file mode 100644 index 0000000..96b65b2 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/NestedCoordinatorLayout.java @@ -0,0 +1,110 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.design.widget.CoordinatorLayout; +import android.support.v4.view.NestedScrollingChild; +import android.support.v4.view.NestedScrollingChildHelper; +import android.util.AttributeSet; +import android.view.View; + +/** + * Created by Kosh on 13 Aug 2016, 1:11 PM + */ + +public class NestedCoordinatorLayout extends CoordinatorLayout implements NestedScrollingChild { + + private NestedScrollingChildHelper mChildHelper; + + public NestedCoordinatorLayout(Context context) { + super(context); + mChildHelper = new NestedScrollingChildHelper(this); + setNestedScrollingEnabled(true); + } + + public NestedCoordinatorLayout(Context context, AttributeSet attrs) { + super(context, attrs); + mChildHelper = new NestedScrollingChildHelper(this); + setNestedScrollingEnabled(true); + } + + public NestedCoordinatorLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mChildHelper = new NestedScrollingChildHelper(this); + setNestedScrollingEnabled(true); + } + + @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { + /* Enable the scrolling behavior of our own children */ + boolean tHandled = super.onStartNestedScroll(child, target, nestedScrollAxes); + /* Enable the scrolling behavior of the parent's other children */ + return startNestedScroll(nestedScrollAxes) || tHandled; + } + + @Override public void onStopNestedScroll(View target) { + /* Disable the scrolling behavior of our own children */ + super.onStopNestedScroll(target); + /* Disable the scrolling behavior of the parent's other children */ + stopNestedScroll(); + } + + @Override public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { + int[][] tConsumed = new int[2][2]; + super.onNestedPreScroll(target, dx, dy, tConsumed[0]); + dispatchNestedPreScroll(dx, dy, tConsumed[1], null); + consumed[0] = tConsumed[0][0] + tConsumed[1][0]; + consumed[1] = tConsumed[0][1] + tConsumed[1][1]; + } + + @Override public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { + super.onNestedScroll(target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); + dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, null); + } + + @Override public boolean onNestedPreFling(View target, float velocityX, float velocityY) { + boolean tHandled = super.onNestedPreFling(target, velocityX, velocityY); + return dispatchNestedPreFling(velocityX, velocityY) || tHandled; + } + + @Override public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) { + boolean tHandled = super.onNestedFling(target, velocityX, velocityY, consumed); + return dispatchNestedFling(velocityX, velocityY, consumed) || tHandled; + } + + @Override public void setNestedScrollingEnabled(boolean enabled) { + mChildHelper.setNestedScrollingEnabled(enabled); + } + + @Override public boolean isNestedScrollingEnabled() { + return mChildHelper.isNestedScrollingEnabled(); + } + + @Override public boolean startNestedScroll(int axes) { + return mChildHelper.startNestedScroll(axes); + } + + @Override public void stopNestedScroll() { + mChildHelper.stopNestedScroll(); + } + + @Override public boolean hasNestedScrollingParent() { + return mChildHelper.hasNestedScrollingParent(); + } + + @Override public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) { + return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, + dyUnconsumed, offsetInWindow); + } + + @Override public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { + return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); + } + + @Override public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { + return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); + } + + @Override public boolean dispatchNestedPreFling(float velocityX, float velocityY) { + return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); + } +} + diff --git a/app/src/main/java/com/fastaccess/ui/widgets/ShadowTransformer.java b/app/src/main/java/com/fastaccess/ui/widgets/ShadowTransformer.java new file mode 100644 index 0000000..de75642 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/ShadowTransformer.java @@ -0,0 +1,115 @@ +package com.fastaccess.ui.widgets; + +import android.support.v4.view.ViewPager; +import android.support.v7.widget.CardView; +import android.view.View; + + +public class ShadowTransformer implements ViewPager.OnPageChangeListener, ViewPager.PageTransformer { + + public interface CardAdapter { + int MAX_ELEVATION_FACTOR = 8; + + float getBaseElevation(); + + CardView getCardViewAt(int position); + + int getCount(); + } + + private ViewPager mViewPager; + private CardAdapter mAdapter; + private float mLastOffset; + private boolean mScalingEnabled; + + public ShadowTransformer(ViewPager viewPager, CardAdapter adapter) { + mViewPager = viewPager; + viewPager.addOnPageChangeListener(this); + mAdapter = adapter; + } + + public void enableScaling(boolean enable) { + if (mScalingEnabled && !enable) { + // shrink main card + CardView currentCard = mAdapter.getCardViewAt(mViewPager.getCurrentItem()); + if (currentCard != null) { + currentCard.animate().scaleY(1); + currentCard.animate().scaleX(1); + } + } else if (!mScalingEnabled && enable) { + // grow main card + CardView currentCard = mAdapter.getCardViewAt(mViewPager.getCurrentItem()); + if (currentCard != null) { + currentCard.animate().scaleY(1.1f); + currentCard.animate().scaleX(1.1f); + } + } + + mScalingEnabled = enable; + } + + @Override public void transformPage(View page, float position) { + + } + + @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int realCurrentPosition; + int nextPosition; + float baseElevation = mAdapter.getBaseElevation(); + float realOffset; + boolean goingLeft = mLastOffset > positionOffset; + + // If we're going backwards, onPageScrolled receives the last position + // instead of the current one + if (goingLeft) { + realCurrentPosition = position + 1; + nextPosition = position; + realOffset = 1 - positionOffset; + } else { + nextPosition = position + 1; + realCurrentPosition = position; + realOffset = positionOffset; + } + + // Avoid crash on overscroll + if (nextPosition > mAdapter.getCount() - 1 + || realCurrentPosition > mAdapter.getCount() - 1) { + return; + } + + CardView currentCard = mAdapter.getCardViewAt(realCurrentPosition); + + // This might be null if a fragment is being used + // and the views weren't created yet + if (currentCard != null) { + if (mScalingEnabled) { + currentCard.setScaleX((float) (1 + 0.1 * (1 - realOffset))); + currentCard.setScaleY((float) (1 + 0.1 * (1 - realOffset))); + } + currentCard.setCardElevation((baseElevation + baseElevation + * (CardAdapter.MAX_ELEVATION_FACTOR - 1) * (1 - realOffset))); + } + + CardView nextCard = mAdapter.getCardViewAt(nextPosition); + + // We might be scrolling fast enough so that the next (or previous) card + // was already destroyed or a fragment might not have been created yet + if (nextCard != null) { + if (mScalingEnabled) { + nextCard.setScaleX((float) (1 + 0.1 * (realOffset))); + nextCard.setScaleY((float) (1 + 0.1 * (realOffset))); + } + nextCard.setCardElevation((baseElevation + baseElevation * (CardAdapter.MAX_ELEVATION_FACTOR - 1) * (realOffset))); + } + + mLastOffset = positionOffset; + } + + @Override public void onPageSelected(int position) { + + } + + @Override public void onPageScrollStateChanged(int state) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/widgets/SwitchView.java b/app/src/main/java/com/fastaccess/ui/widgets/SwitchView.java new file mode 100644 index 0000000..f7c74ef --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/SwitchView.java @@ -0,0 +1,35 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.support.v7.widget.SwitchCompat; +import android.util.AttributeSet; + +import com.fastaccess.helper.TypeFaceHelper; + + +/** + * Created by Kosh on 8/18/2015. copyrights are reserved + */ +public class SwitchView extends SwitchCompat { + + public SwitchView(Context context) { + super(context); + init(); + } + + public SwitchView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + + } + + public SwitchView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (isInEditMode()) return; + TypeFaceHelper.applyTypeface(this); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/ViewPagerView.java b/app/src/main/java/com/fastaccess/ui/widgets/ViewPagerView.java new file mode 100644 index 0000000..a489c4d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/ViewPagerView.java @@ -0,0 +1,48 @@ +package com.fastaccess.ui.widgets; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; + +import com.fastaccess.R; + + +/** + * Created by kosh20111 on 10/8/2015. + *

+ * Viewpager that has scrolling animation by default + */ +public class ViewPagerView extends ViewPager { + + private boolean isEnabled; + + public ViewPagerView(Context context) { + super(context, null); + } + + public ViewPagerView(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewPagerView); + isEnabled = a.getBoolean(R.styleable.ViewPagerView_isEnabled, true); + a.recycle(); + } + + @Override public boolean isEnabled() { + return isEnabled; + } + + @Override public void setEnabled(boolean enabled) { + this.isEnabled = enabled; + requestLayout(); + } + + @Override public boolean onTouchEvent(MotionEvent event) { + return !isEnabled() || super.onTouchEvent(event); + } + + @Override public boolean onInterceptTouchEvent(MotionEvent event) { + return isEnabled() && super.onInterceptTouchEvent(event); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/dialog/MessageDialogView.java b/app/src/main/java/com/fastaccess/ui/widgets/dialog/MessageDialogView.java new file mode 100644 index 0000000..6d28c5d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/dialog/MessageDialogView.java @@ -0,0 +1,94 @@ +package com.fastaccess.ui.widgets.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.view.View; + +import com.fastaccess.R; +import com.fastaccess.helper.Bundler; +import com.fastaccess.ui.base.BaseBottomSheetDialog; +import com.fastaccess.ui.widgets.FontTextView; + +import butterknife.BindView; +import butterknife.OnClick; + +/** + * Created by Kosh on 16 Sep 2016, 2:15 PM + */ + +public class MessageDialogView extends BaseBottomSheetDialog { + + public interface MessageDialogViewActionCallback { + void onMessageDialogActionClicked(boolean isOk, int requestCode); + + void onDialogDismissed(); + } + + @BindView(R.id.title) FontTextView title; + + @BindView(R.id.message) FontTextView message; + + @Nullable private MessageDialogViewActionCallback callback; + + @Override public void onAttach(Context context) { + super.onAttach(context); + if (getParentFragment() != null) { + if (getParentFragment() instanceof MessageDialogViewActionCallback) { + callback = (MessageDialogViewActionCallback) getParentFragment(); + } + } else if (context instanceof MessageDialogViewActionCallback) { + callback = (MessageDialogViewActionCallback) context; + } + } + + @Override public void onDetach() { + super.onDetach(); + callback = null; + } + + @OnClick({R.id.cancel, R.id.ok}) public void onClick(View view) { + if (callback != null) { + isAlreadyHidden = true; + callback.onMessageDialogActionClicked(view.getId() == R.id.ok, getArguments().getInt("requestCode")); + } + dismiss(); + } + + @Override protected int layoutRes() { + return R.layout.message_dialog; + } + + @Override protected void onViewCreated(@NonNull View view) { + Bundle bundle = getArguments(); + title.setText(bundle.getInt("bundleTitle")); + message.setText(bundle.getInt("bundleMsg")); + } + + @Override protected void onDismissedByScrolling() { + super.onDismissedByScrolling(); + if (callback != null) callback.onDialogDismissed(); + } + + @Override protected void onHidden() { + if (callback != null) callback.onDialogDismissed(); + super.onHidden(); + } + + @NonNull public static MessageDialogView newInstance(@StringRes int bundleTitle, @StringRes int bundleMsg) { + return newInstance(bundleTitle, bundleMsg, 0); + } + + @NonNull public static MessageDialogView newInstance(@StringRes int bundleTitle, @StringRes int bundleMsg, int requestCode) { + MessageDialogView messageDialogView = new MessageDialogView(); + messageDialogView.setArguments(Bundler + .start() + .put("bundleTitle", bundleTitle) + .put("bundleMsg", bundleMsg) + .put("requestCode", requestCode) + .end()); + return messageDialogView; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingLayout.java b/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingLayout.java new file mode 100644 index 0000000..c959036 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingLayout.java @@ -0,0 +1,49 @@ +package com.fastaccess.ui.widgets.floating; + +import android.content.Context; +import android.content.res.Configuration; +import android.util.AttributeSet; +import android.view.KeyEvent; + +import com.fastaccess.ui.modules.floating.folders.drawer.FloatingDrawerMvp; + +import io.codetail.widget.RevealFrameLayout; + +/** + * Created by Kosh on 22 Oct 2016, 12:30 PM + */ + +public class FloatingLayout extends RevealFrameLayout { + + private FloatingDrawerMvp.View viewCallback; + + public FloatingLayout(Context context) { + this(context, null); + } + + public FloatingLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FloatingLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (viewCallback != null && isShown()) { + viewCallback.onBackPressed(); + } + } + return super.dispatchKeyEvent(event); + } + + @Override protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (viewCallback != null) viewCallback.onConfigChanged(newConfig.orientation); + } + + public void setViewCallback(FloatingDrawerMvp.View viewCallback) { + this.viewCallback = viewCallback; + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingTouchCallback.java b/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingTouchCallback.java new file mode 100644 index 0000000..e1a83b5 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingTouchCallback.java @@ -0,0 +1,25 @@ +package com.fastaccess.ui.widgets.floating; + +/** + * Created by Kosh on 14 Oct 2016, 7:31 PM + */ + +public interface FloatingTouchCallback { + void onViewMoving(int x, int y); + + void onSingleTapped(); + + void onDoubleTapped(); + + void onLongPressed(); + + void onSwipe(int swipeDirection); + + void onBackPressed(); + + void onTouchOutside(); + + void onStoppedMoving(); + + void onConfigChanged(int orientation); +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingView.java b/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingView.java new file mode 100644 index 0000000..88585da --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/floating/FloatingView.java @@ -0,0 +1,175 @@ +package com.fastaccess.ui.widgets.floating; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.support.v4.graphics.drawable.RoundedBitmapDrawable; +import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.widget.ImageView; + +import com.fastaccess.R; +import com.fastaccess.helper.InputHelper; +import com.fastaccess.helper.Logger; +import com.fastaccess.helper.PrefConstant; +import com.fastaccess.helper.PrefHelper; +import com.github.nisrulz.sensey.Sensey; +import com.github.nisrulz.sensey.TouchTypeDetector; + +import java.io.File; + +import io.codetail.widget.RevealFrameLayout; + +/** + * Created by Kosh on 14 Oct 2016, 7:11 PM + */ + +@SuppressLint("ViewConstructor") +public class FloatingView extends RevealFrameLayout implements TouchTypeDetector.TouchTypListener { + + private int initialX; + private int initialY; + private float initialTouchX; + private float initialTouchY; + private ImageView imageView; + private FloatingTouchCallback callback; + + public FloatingView(Context context, FloatingTouchCallback callback) { + super(context); + this.callback = callback; + imageView = new ImageView(context); + imageView.setAdjustViewBounds(true); + setupImageView(); + addView(imageView); + } + + @Override protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Sensey.getInstance().init(getContext().getApplicationContext()); + Sensey.getInstance().startTouchTypeDetection(this); + } + + @Override protected void onDetachedFromWindow() { + Sensey.getInstance().stopTouchTypeDetection(); + callback = null; + removeAllViews(); + super.onDetachedFromWindow(); + } + + @Override public void onTwoFingerSingleTap() {}//op-out + + @Override public void onThreeFingerSingleTap() {}//op-out + + @Override public void onDoubleTap() { + callback.onDoubleTapped(); + } + + @Override public void onScroll(int scrollDirection) {}//op-out + + @Override public void onSingleTap() { + callback.onSingleTapped(); + } + + @Override public void onSwipe(int swipeDirection) { + callback.onSwipe(swipeDirection); + } + + @Override public void onLongPress() { + callback.onLongPressed(); + } + + @Override public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + initialTouchX = event.getRawX(); + initialTouchY = event.getRawY(); + imageView.setPressed(true); + break; + case MotionEvent.ACTION_UP: + callback.onStoppedMoving(); + onMoving(false); + imageView.setPressed(false); + break; + case MotionEvent.ACTION_MOVE: + onMoving(true); + callback.onViewMoving(initialX + (int) (event.getRawX() - initialTouchX), initialY + (int) (event.getRawY() - initialTouchY)); + break; + case MotionEvent.ACTION_OUTSIDE: + callback.onTouchOutside(); + imageView.setPressed(false); + break; + } + return super.onTouchEvent(event); + } + + @Override public boolean dispatchTouchEvent(MotionEvent event) { + Sensey.getInstance().setupDispatchTouchEvent(event); + return super.dispatchTouchEvent(event); + } + + @Override public boolean dispatchKeyEvent(KeyEvent event) { + Logger.e(); + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + callback.onBackPressed(); + } + return super.dispatchKeyEvent(event); + } + + @Override protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + callback.onConfigChanged(newConfig.orientation); + } + + public void setupImageView() { + if (imageView != null) { + String path = PrefHelper.getString(PrefConstant.CUSTOM_ICON); + if (!InputHelper.isEmpty(path)) { + path = Uri.decode(PrefHelper.getString(PrefConstant.CUSTOM_ICON)); + boolean fileExists = new File(path).exists(); + if (fileExists) { + imageView.setImageDrawable(null); + Bitmap src = BitmapFactory.decodeFile(path); + if (src == null) { + imageView.setImageResource(R.drawable.ic_app_drawer_icon); + onMoving(false); + return; + } + RoundedBitmapDrawable dr = RoundedBitmapDrawableFactory.create(getResources(), src); + dr.setCornerRadius(Math.max(src.getWidth(), src.getHeight()) / 2.0f); + imageView.setImageDrawable(dr); + return; + } + } + imageView.setImageResource(R.drawable.ic_app_drawer_icon); + onMoving(false); + } + } + + private void onMoving(boolean isMoving) { + if (imageView == null) return; + if (isMoving) { + imageView.setImageAlpha(255); + } else { + boolean isAutoTransparent = PrefHelper.getBoolean(PrefConstant.AUTO_TRANS); + int alpha = PrefHelper.getInt(PrefConstant.ICON_ALPHA); + if (isAutoTransparent) { + imageView.setImageAlpha(alpha == 0 ? 100 : alpha); + } else { + imageView.setImageAlpha(255); + } + } + } + + public void setInitialY(int initialY) { + this.initialY = initialY; + } + + public void setInitialX(int initialX) { + this.initialX = initialX; + + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/BaseRecyclerAdapter.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/BaseRecyclerAdapter.java new file mode 100644 index 0000000..bbbfab1 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/BaseRecyclerAdapter.java @@ -0,0 +1,129 @@ +package com.fastaccess.ui.widgets.recyclerview; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +import com.fastaccess.helper.AnimHelper; + +import java.util.List; + +/** + * Created by Kosh on 17 May 2016, 7:10 PM + */ +public abstract class BaseRecyclerAdapter> extends RecyclerView.Adapter { + + @NonNull private List data; + @Nullable public P listener; + private int lastKnowingPosition = -1; + private boolean enableAnimation = true; + + public BaseRecyclerAdapter(@NonNull List data) { + this(data, null); + } + + public BaseRecyclerAdapter(@NonNull List data, @Nullable P listener) { + this.data = data; + this.listener = listener; + } + + protected abstract VH viewHolder(ViewGroup parent, int viewType); + + protected abstract void onBindView(VH holder, int position); + + @NonNull public List getData() { + return data; + } + + public M getItem(int position) { + return data.get(position); + } + + public int getItem(M t) { + return data.indexOf(t); + } + + @Override public VH onCreateViewHolder(ViewGroup parent, int viewType) { + return viewHolder(parent, viewType); + } + + @Override public void onBindViewHolder(VH holder, int position) { + animate(holder); + onBindView(holder, position); + } + + @Override public int getItemCount() { + return data.size(); + } + + private void animate(VH holder) { + int position = holder.getLayoutPosition(); + if (isEnableAnimation() /*&& position > lastKnowingPosition*/) { + AnimHelper.startBeatsAnimation(holder.itemView); + lastKnowingPosition = position; + } + } + + public void insertItems(List items) { + data.clear(); + addItems(items); + } + + public void addItem(M item) { + addItem(item, getItemCount()); + } + + public void addItem(M item, int position) { + data.add(position, item); + notifyItemInserted(position); + } + + public void addItems(List items) { + data.addAll(items); + notifyDataSetChanged(); + } + + public void removeItem(int position) { + data.remove(position); + notifyItemRemoved(position); + } + + public void removeItem(M item) { + int position = data.indexOf(item); + removeItem(position); + } + + public void removeItems(List items) { +// int prevSize = data.size(); + data.removeAll(items); + notifyDataSetChanged(); +// notifyItemRangeRemoved(prevSize, Math.abs(data.size() - prevSize)); + } + + public void swapItem(M model) { + int index = getItem(model); + swapItem(model, index); + } + + public void swapItem(M model, int position) { + data.set(position, model); + notifyDataSetChanged(); + } + + public void clear() { + data.clear(); + notifyItemRangeRemoved(0, getItemCount()); + } + + public void setEnableAnimation(boolean enableAnimation) { + this.enableAnimation = enableAnimation; + notifyDataSetChanged(); + } + + public boolean isEnableAnimation() { + return enableAnimation; + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/BaseViewHolder.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/BaseViewHolder.java new file mode 100644 index 0000000..aeaa925 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/BaseViewHolder.java @@ -0,0 +1,54 @@ +package com.fastaccess.ui.widgets.recyclerview; + +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import butterknife.ButterKnife; + +/** + * Created by Kosh on 17 May 2016, 7:13 PM + */ +public abstract class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + + public interface OnItemClickListener { + void onItemClick(int position, View v, T item); + + void onItemLongClick(int position, View v, T item); + } + + protected BaseRecyclerAdapter adapter; + + public static View getView(ViewGroup parent, @LayoutRes int layoutRes) { + return LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false); + } + + public BaseViewHolder(@NonNull View itemView, @Nullable BaseRecyclerAdapter adapter) { + super(itemView); + ButterKnife.bind(this, itemView); + itemView.setOnClickListener(this); + itemView.setOnLongClickListener(this); + this.adapter = adapter; + } + + @SuppressWarnings("unchecked") @Override public void onClick(View v) { + int position = getAdapterPosition(); + if (adapter != null && adapter.listener != null) { + adapter.listener.onItemClick(position, v, adapter.getItem(position)); + } + } + + @SuppressWarnings("unchecked") @Override public boolean onLongClick(View v) { + int position = getAdapterPosition(); + if (adapter != null && adapter.listener != null) { + adapter.listener.onItemLongClick(position, v, adapter.getItem(position)); + } + return true; + } + + public abstract void bind(@NonNull T t); +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/DynamicRecyclerView.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/DynamicRecyclerView.java new file mode 100644 index 0000000..441a46c --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/DynamicRecyclerView.java @@ -0,0 +1,133 @@ +package com.fastaccess.ui.widgets.recyclerview; + +import android.content.Context; +import android.content.res.TypedArray; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; + +import com.fastaccess.R; + +import static android.R.attr.columnWidth; + + +/** + * Created by Kosh on 9/24/2015. copyrights are reserved + *

+ * recyclerview which will showParentOrSelf/showParentOrSelf itself base on adapter + */ +public class DynamicRecyclerView extends RecyclerView { + + private View emptyView; + private int iconSize; + @Nullable private View parentView; + + @NonNull private AdapterDataObserver observer = new AdapterDataObserver() { + @Override public void onChanged() { + showEmptyView(); + } + + @Override public void onItemRangeInserted(int positionStart, int itemCount) { + super.onItemRangeInserted(positionStart, itemCount); + showEmptyView(); + } + + @Override public void onItemRangeRemoved(int positionStart, int itemCount) { + super.onItemRangeRemoved(positionStart, itemCount); + showEmptyView(); + } + }; + + public DynamicRecyclerView(Context context) { + this(context, null); + } + + public DynamicRecyclerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public DynamicRecyclerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + if (attrs != null) { + int[] attrsArray = {columnWidth}; + TypedArray array = context.obtainStyledAttributes(attrs, attrsArray); + iconSize = array.getDimensionPixelSize(0, -1); + if (iconSize > 0) { + iconSize += getResources().getDimensionPixelSize(R.dimen.spacing_micro); + } + array.recycle(); + } + } + + public void showEmptyView() { + Adapter adapter = getAdapter(); + if (adapter != null) { + if (emptyView != null) { + if (adapter.getItemCount() == 0) { + showParentOrSelf(false); + } else { + showParentOrSelf(true); + } + } + } else { + if (emptyView != null) { + showParentOrSelf(false); + } + } + } + + @Override public void setAdapter(@Nullable Adapter adapter) { + super.setAdapter(adapter); + if (adapter != null) { + adapter.registerAdapterDataObserver(observer); + observer.onChanged(); + } + } + + @Override protected void onMeasure(int widthSpec, int heightSpec) { + super.onMeasure(widthSpec, heightSpec); + int width = MeasureSpec.getSize(widthSpec); + if (iconSize > 0 && width != 0) { + int spanCount = Math.max(1, width / iconSize); + if (getLayoutManager() instanceof GridLayoutManager) { + ((GridLayoutManager) getLayoutManager()).setSpanCount(spanCount); + getLayoutManager().requestLayout(); + } + } + } + + private void showParentOrSelf(boolean show) { + if (parentView == null) { + setVisibility(show ? VISIBLE : GONE); + } else { + parentView.setVisibility(show ? VISIBLE : GONE); + } + emptyView.setVisibility(!show ? VISIBLE : GONE); + } + + public void setEmptyView(@NonNull View emptyView, @Nullable View parentView) { + this.emptyView = emptyView; + this.parentView = parentView; + showEmptyView(); + } + + public void setEmptyView(@NonNull View emptyView) { + setEmptyView(emptyView, null); + } + + public void hideProgress(@NonNull View view) { + view.setVisibility(GONE); + } + + public void showProgress(@NonNull View view) { + view.setVisibility(VISIBLE); + } + + public void setIconSize(int iconSize) { + this.iconSize = iconSize; + invalidate(); + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/GridManager.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/GridManager.java new file mode 100644 index 0000000..246a506 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/GridManager.java @@ -0,0 +1,30 @@ +package com.fastaccess.ui.widgets.recyclerview.layout_manager; + +import android.content.Context; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +/** + * Created by Kosh on 17 May 2016, 10:02 PM + */ +public class GridManager extends GridLayoutManager { + + public GridManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public GridManager(Context context, int spanCount) { + super(context, spanCount); + } + + public GridManager(Context context, int spanCount, int orientation, boolean reverseLayout) { + super(context, spanCount, orientation, reverseLayout); + } + + @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + super.onLayoutChildren(recycler, state); + } catch (Exception ignored) {} + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/LinearManager.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/LinearManager.java new file mode 100644 index 0000000..9a78780 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/LinearManager.java @@ -0,0 +1,30 @@ +package com.fastaccess.ui.widgets.recyclerview.layout_manager; + +import android.content.Context; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; + +/** + * Created by Kosh on 17 May 2016, 10:02 PM + */ +public class LinearManager extends LinearLayoutManager { + + public LinearManager(Context context) { + super(context); + } + + public LinearManager(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + public LinearManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + super.onLayoutChildren(recycler, state); + } catch (IndexOutOfBoundsException ignored) {} + } +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/StaggeredManager.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/StaggeredManager.java new file mode 100644 index 0000000..659fba2 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/layout_manager/StaggeredManager.java @@ -0,0 +1,27 @@ +package com.fastaccess.ui.widgets.recyclerview.layout_manager; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.AttributeSet; + +/** + * Created by Kosh on 17 May 2016, 10:02 PM + */ +public class StaggeredManager extends StaggeredGridLayoutManager { + + public StaggeredManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public StaggeredManager(int spanCount, int orientation) { + super(spanCount, orientation); + } + + @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + super.onLayoutChildren(recycler, state); + } catch (IndexOutOfBoundsException ignored) {} + } + +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/ItemTouchHelperAdapter.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/ItemTouchHelperAdapter.java new file mode 100644 index 0000000..3a7333d --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/ItemTouchHelperAdapter.java @@ -0,0 +1,9 @@ +package com.fastaccess.ui.widgets.recyclerview.touch; + +public interface ItemTouchHelperAdapter { + void onItemMove(int fromPosition, int toPosition); + + void onItemDismiss(int position); + + void onItemStoppedMoving(); +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/ItemTouchHelperViewHolder.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/ItemTouchHelperViewHolder.java new file mode 100644 index 0000000..2348cba --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/ItemTouchHelperViewHolder.java @@ -0,0 +1,7 @@ +package com.fastaccess.ui.widgets.recyclerview.touch; + +public interface ItemTouchHelperViewHolder { + void onItemSelected(); + + void onItemClear(); +} diff --git a/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/SimpleItemTouchHelperCallback.java b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/SimpleItemTouchHelperCallback.java new file mode 100644 index 0000000..77ce7e3 --- /dev/null +++ b/app/src/main/java/com/fastaccess/ui/widgets/recyclerview/touch/SimpleItemTouchHelperCallback.java @@ -0,0 +1,86 @@ +package com.fastaccess.ui.widgets.recyclerview.touch; + +import android.graphics.Canvas; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.View; + +public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback { + + public static final float ALPHA_FULL = 1.0f; + + private final ItemTouchHelperAdapter adapter; + private boolean isSwipeable; + + public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter, boolean isSwipeable) { + this.adapter = adapter; + this.isSwipeable = isSwipeable; + } + + @Override public boolean isLongPressDragEnabled() { + return true; + } + + @Override public boolean isItemViewSwipeEnabled() { + return isSwipeable; + } + + @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + if (recyclerView.getLayoutManager() instanceof GridLayoutManager) { + final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT; + final int swipeFlags = 0; + return makeMovementFlags(dragFlags, swipeFlags); + } else { + final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; + final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END; + return makeMovementFlags(dragFlags, swipeFlags); + } + } + + @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) { + if (source.getItemViewType() != target.getItemViewType()) { + return false; + } + adapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition()); + return true; + } + + @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) { + if (viewHolder instanceof ItemTouchHelperViewHolder) { + adapter.onItemDismiss(viewHolder.getAdapterPosition()); + } + } + + @Override public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, + float dX, float dY, int actionState, boolean isCurrentlyActive) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { + if (viewHolder instanceof ItemTouchHelperViewHolder) { + View itemView = viewHolder.itemView; + final float alpha = ALPHA_FULL - Math.abs(dX) / (float) itemView.getWidth(); + itemView.setAlpha(alpha); + } + } + } + + @Override public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder instanceof ItemTouchHelperViewHolder) { + ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemSelected(); + } + } + super.onSelectedChanged(viewHolder, actionState); + } + + @Override public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + if (viewHolder instanceof ItemTouchHelperViewHolder) { + viewHolder.itemView.setAlpha(ALPHA_FULL); + ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemClear(); + } + adapter.onItemStoppedMoving(); + } +} diff --git a/app/src/main/res/color/preference_primary_color.xml b/app/src/main/res/color/preference_primary_color.xml new file mode 100644 index 0000000..4f97614 --- /dev/null +++ b/app/src/main/res/color/preference_primary_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/color/preference_secondry_color.xml b/app/src/main/res/color/preference_secondry_color.xml new file mode 100644 index 0000000..4bf3325 --- /dev/null +++ b/app/src/main/res/color/preference_secondry_color.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable-hdpi/folder_background.9.png b/app/src/main/res/drawable-hdpi/folder_background.9.png new file mode 100755 index 0000000..9db01bf Binary files /dev/null and b/app/src/main/res/drawable-hdpi/folder_background.9.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_drawer_normal.png b/app/src/main/res/drawable-hdpi/ic_drawer_normal.png new file mode 100755 index 0000000..732dada Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_drawer_normal.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_drawer_pressed.png b/app/src/main/res/drawable-hdpi/ic_drawer_pressed.png new file mode 100755 index 0000000..59d3cb2 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_drawer_pressed.png differ diff --git a/app/src/main/res/drawable-mdpi/folder_background.9.png b/app/src/main/res/drawable-mdpi/folder_background.9.png new file mode 100755 index 0000000..4cc700c Binary files /dev/null and b/app/src/main/res/drawable-mdpi/folder_background.9.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_drawer_normal.png b/app/src/main/res/drawable-mdpi/ic_drawer_normal.png new file mode 100755 index 0000000..a68558f Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_drawer_normal.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_drawer_pressed.png b/app/src/main/res/drawable-mdpi/ic_drawer_pressed.png new file mode 100755 index 0000000..4a9bf06 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_drawer_pressed.png differ diff --git a/app/src/main/res/drawable-nodpi/header_background.jpeg b/app/src/main/res/drawable-nodpi/header_background.jpeg new file mode 100644 index 0000000..107f576 Binary files /dev/null and b/app/src/main/res/drawable-nodpi/header_background.jpeg differ diff --git a/app/src/main/res/drawable-nodpi/ic_fa_splash.png b/app/src/main/res/drawable-nodpi/ic_fa_splash.png new file mode 100644 index 0000000..5ff210d Binary files /dev/null and b/app/src/main/res/drawable-nodpi/ic_fa_splash.png differ diff --git a/app/src/main/res/drawable-xhdpi/folder_background.9.png b/app/src/main/res/drawable-xhdpi/folder_background.9.png new file mode 100755 index 0000000..5219b43 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/folder_background.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_drawer_normal.png b/app/src/main/res/drawable-xhdpi/ic_drawer_normal.png new file mode 100755 index 0000000..d8641fd Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_drawer_normal.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_drawer_pressed.png b/app/src/main/res/drawable-xhdpi/ic_drawer_pressed.png new file mode 100755 index 0000000..080e70f Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_drawer_pressed.png differ diff --git a/app/src/main/res/drawable-xxhdpi/folder_background.9.png b/app/src/main/res/drawable-xxhdpi/folder_background.9.png new file mode 100755 index 0000000..15f1d52 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/folder_background.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_drawer_normal.png b/app/src/main/res/drawable-xxhdpi/ic_drawer_normal.png new file mode 100755 index 0000000..7b260fe Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_drawer_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_drawer_pressed.png b/app/src/main/res/drawable-xxhdpi/ic_drawer_pressed.png new file mode 100755 index 0000000..aa600e3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_drawer_pressed.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/folder_background.9.png b/app/src/main/res/drawable-xxxhdpi/folder_background.9.png new file mode 100755 index 0000000..657ce1d Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/folder_background.9.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_drawer_normal.png b/app/src/main/res/drawable-xxxhdpi/ic_drawer_normal.png new file mode 100755 index 0000000..8114de9 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_drawer_normal.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_drawer_pressed.png b/app/src/main/res/drawable-xxxhdpi/ic_drawer_pressed.png new file mode 100755 index 0000000..f386506 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_drawer_pressed.png differ diff --git a/app/src/main/res/drawable/bottom_border.xml b/app/src/main/res/drawable/bottom_border.xml new file mode 100644 index 0000000..86af043 --- /dev/null +++ b/app/src/main/res/drawable/bottom_border.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml new file mode 100644 index 0000000..c4b0286 --- /dev/null +++ b/app/src/main/res/drawable/ic_add.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_app_drawer_icon.xml b/app/src/main/res/drawable/ic_app_drawer_icon.xml new file mode 100644 index 0000000..c40b424 --- /dev/null +++ b/app/src/main/res/drawable/ic_app_drawer_icon.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_apps_drawer.xml b/app/src/main/res/drawable/ic_apps_drawer.xml new file mode 100644 index 0000000..fb22642 --- /dev/null +++ b/app/src/main/res/drawable/ic_apps_drawer.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_right.xml b/app/src/main/res/drawable/ic_arrow_right.xml new file mode 100644 index 0000000..bf3cb5d --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 0000000..38fbc26 --- /dev/null +++ b/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_back_pressed.xml b/app/src/main/res/drawable/ic_back_pressed.xml new file mode 100644 index 0000000..ec2d764 --- /dev/null +++ b/app/src/main/res/drawable/ic_back_pressed.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_back_selector.xml b/app/src/main/res/drawable/ic_back_selector.xml new file mode 100644 index 0000000..2fa27d7 --- /dev/null +++ b/app/src/main/res/drawable/ic_back_selector.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_backup.xml b/app/src/main/res/drawable/ic_backup.xml new file mode 100644 index 0000000..9b51834 --- /dev/null +++ b/app/src/main/res/drawable/ic_backup.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_clear.xml b/app/src/main/res/drawable/ic_clear.xml new file mode 100644 index 0000000..d11cc5c --- /dev/null +++ b/app/src/main/res/drawable/ic_clear.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_done.xml b/app/src/main/res/drawable/ic_done.xml new file mode 100644 index 0000000..99caef9 --- /dev/null +++ b/app/src/main/res/drawable/ic_done.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_fa.xml b/app/src/main/res/drawable/ic_fa.xml new file mode 100644 index 0000000..b93bab6 --- /dev/null +++ b/app/src/main/res/drawable/ic_fa.xml @@ -0,0 +1,66 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_fa_notification.xml b/app/src/main/res/drawable/ic_fa_notification.xml new file mode 100644 index 0000000..6ce4451 --- /dev/null +++ b/app/src/main/res/drawable/ic_fa_notification.xml @@ -0,0 +1,66 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_folder.xml b/app/src/main/res/drawable/ic_folder.xml new file mode 100644 index 0000000..4e958a6 --- /dev/null +++ b/app/src/main/res/drawable/ic_folder.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu.xml b/app/src/main/res/drawable/ic_menu.xml new file mode 100644 index 0000000..1d81586 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_nav_right.xml b/app/src/main/res/drawable/ic_nav_right.xml new file mode 100644 index 0000000..a3d1622 --- /dev/null +++ b/app/src/main/res/drawable/ic_nav_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_notification.png b/app/src/main/res/drawable/ic_notification.png new file mode 100644 index 0000000..caaf908 Binary files /dev/null and b/app/src/main/res/drawable/ic_notification.png differ diff --git a/app/src/main/res/drawable/ic_play.xml b/app/src/main/res/drawable/ic_play.xml new file mode 100644 index 0000000..bf9b895 --- /dev/null +++ b/app/src/main/res/drawable/ic_play.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_restore.xml b/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 0000000..5a3137b --- /dev/null +++ b/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_sad_face.xml b/app/src/main/res/drawable/ic_sad_face.xml new file mode 100644 index 0000000..e0be02e --- /dev/null +++ b/app/src/main/res/drawable/ic_sad_face.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..b1ab121 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml new file mode 100644 index 0000000..4cdbe19 --- /dev/null +++ b/app/src/main/res/drawable/ic_select_all.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_selected_apps.xml b/app/src/main/res/drawable/ic_selected_apps.xml new file mode 100644 index 0000000..fac8f73 --- /dev/null +++ b/app/src/main/res/drawable/ic_selected_apps.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..ace746c --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 0000000..7cebafd --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_stop.xml b/app/src/main/res/drawable/ic_stop.xml new file mode 100644 index 0000000..c428d72 --- /dev/null +++ b/app/src/main/res/drawable/ic_stop.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_trash.xml b/app/src/main/res/drawable/ic_trash.xml new file mode 100644 index 0000000..689db8c --- /dev/null +++ b/app/src/main/res/drawable/ic_trash.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/left_border.xml b/app/src/main/res/drawable/left_border.xml new file mode 100644 index 0000000..8bb9ccf --- /dev/null +++ b/app/src/main/res/drawable/left_border.xml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/list_divider.xml b/app/src/main/res/drawable/list_divider.xml new file mode 100644 index 0000000..b9fa9a8 --- /dev/null +++ b/app/src/main/res/drawable/list_divider.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/right_border.xml b/app/src/main/res/drawable/right_border.xml new file mode 100644 index 0000000..86d8d62 --- /dev/null +++ b/app/src/main/res/drawable/right_border.xml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_screen_drawable.xml b/app/src/main/res/drawable/splash_screen_drawable.xml new file mode 100644 index 0000000..ff5f009 --- /dev/null +++ b/app/src/main/res/drawable/splash_screen_drawable.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/toolbar_shadow.xml b/app/src/main/res/drawable/toolbar_shadow.xml new file mode 100644 index 0000000..8a84c33 --- /dev/null +++ b/app/src/main/res/drawable/toolbar_shadow.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/drawable/top_border.xml b/app/src/main/res/drawable/top_border.xml new file mode 100644 index 0000000..b84bd22 --- /dev/null +++ b/app/src/main/res/drawable/top_border.xml @@ -0,0 +1,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/top_shadow.xml b/app/src/main/res/drawable/top_shadow.xml new file mode 100644 index 0000000..9670ce3 --- /dev/null +++ b/app/src/main/res/drawable/top_shadow.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/src/main/res/layouts/floating_layouts/layout/floating_folder_layout.xml b/app/src/main/res/layouts/floating_layouts/layout/floating_folder_layout.xml new file mode 100644 index 0000000..fa7e4dc --- /dev/null +++ b/app/src/main/res/layouts/floating_layouts/layout/floating_folder_layout.xml @@ -0,0 +1,38 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/floating_layouts/layout/horizontal_layout.xml b/app/src/main/res/layouts/floating_layouts/layout/horizontal_layout.xml new file mode 100644 index 0000000..9e41380 --- /dev/null +++ b/app/src/main/res/layouts/floating_layouts/layout/horizontal_layout.xml @@ -0,0 +1,36 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/floating_layouts/layout/transparency_layout.xml b/app/src/main/res/layouts/floating_layouts/layout/transparency_layout.xml new file mode 100644 index 0000000..5918af1 --- /dev/null +++ b/app/src/main/res/layouts/floating_layouts/layout/transparency_layout.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/floating_layouts/layout/vertical_layout.xml b/app/src/main/res/layouts/floating_layouts/layout/vertical_layout.xml new file mode 100644 index 0000000..881628b --- /dev/null +++ b/app/src/main/res/layouts/floating_layouts/layout/vertical_layout.xml @@ -0,0 +1,33 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/main_layouts/layout/activity_main.xml b/app/src/main/res/layouts/main_layouts/layout/activity_main.xml new file mode 100644 index 0000000..0df5f33 --- /dev/null +++ b/app/src/main/res/layouts/main_layouts/layout/activity_main.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layouts/main_layouts/layout/app_details_layout.xml b/app/src/main/res/layouts/main_layouts/layout/app_details_layout.xml new file mode 100644 index 0000000..1fbedb8 --- /dev/null +++ b/app/src/main/res/layouts/main_layouts/layout/app_details_layout.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/main_layouts/layout/create_edit_folder.xml b/app/src/main/res/layouts/main_layouts/layout/create_edit_folder.xml new file mode 100644 index 0000000..a78199f --- /dev/null +++ b/app/src/main/res/layouts/main_layouts/layout/create_edit_folder.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/main_layouts/layout/select_folder_apps_layout.xml b/app/src/main/res/layouts/main_layouts/layout/select_folder_apps_layout.xml new file mode 100644 index 0000000..82589ea --- /dev/null +++ b/app/src/main/res/layouts/main_layouts/layout/select_folder_apps_layout.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/main_layouts/layout/settings_layout.xml b/app/src/main/res/layouts/main_layouts/layout/settings_layout.xml new file mode 100644 index 0000000..3dbde9d --- /dev/null +++ b/app/src/main/res/layouts/main_layouts/layout/settings_layout.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/main_layouts/layout/sign_in_layout.xml b/app/src/main/res/layouts/main_layouts/layout/sign_in_layout.xml new file mode 100644 index 0000000..c3e5532 --- /dev/null +++ b/app/src/main/res/layouts/main_layouts/layout/sign_in_layout.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/appbar_elevation.xml b/app/src/main/res/layouts/other_layouts/layout/appbar_elevation.xml new file mode 100644 index 0000000..61e091c --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/appbar_elevation.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/appbar_elevation_dark.xml b/app/src/main/res/layouts/other_layouts/layout/appbar_elevation_dark.xml new file mode 100644 index 0000000..ccf22f8 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/appbar_elevation_dark.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/appbar_search_elevation.xml b/app/src/main/res/layouts/other_layouts/layout/appbar_search_elevation.xml new file mode 100644 index 0000000..ad27114 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/appbar_search_elevation.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/appbar_tabbed_elevation.xml b/app/src/main/res/layouts/other_layouts/layout/appbar_tabbed_elevation.xml new file mode 100644 index 0000000..c13b315 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/appbar_tabbed_elevation.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/crop_image_layout.xml b/app/src/main/res/layouts/other_layouts/layout/crop_image_layout.xml new file mode 100644 index 0000000..d7e6779 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/crop_image_layout.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/drawer_header.xml b/app/src/main/res/layouts/other_layouts/layout/drawer_header.xml new file mode 100644 index 0000000..149c696 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/drawer_header.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/empty_layout.xml b/app/src/main/res/layouts/other_layouts/layout/empty_layout.xml new file mode 100644 index 0000000..635d458 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/empty_layout.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/grid_list.xml b/app/src/main/res/layouts/other_layouts/layout/grid_list.xml new file mode 100644 index 0000000..c07b4b6 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/grid_list.xml @@ -0,0 +1,20 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/icon_chooser_layout.xml b/app/src/main/res/layouts/other_layouts/layout/icon_chooser_layout.xml new file mode 100644 index 0000000..732d396 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/icon_chooser_layout.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/message_dialog.xml b/app/src/main/res/layouts/other_layouts/layout/message_dialog.xml new file mode 100644 index 0000000..7f616cf --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/message_dialog.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/other_layouts/layout/progress_layout.xml b/app/src/main/res/layouts/other_layouts/layout/progress_layout.xml new file mode 100644 index 0000000..b4b9728 --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/progress_layout.xml @@ -0,0 +1,18 @@ + + diff --git a/app/src/main/res/layouts/other_layouts/layout/small_grid_list.xml b/app/src/main/res/layouts/other_layouts/layout/small_grid_list.xml new file mode 100644 index 0000000..3be1fec --- /dev/null +++ b/app/src/main/res/layouts/other_layouts/layout/small_grid_list.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/app_details_header.xml b/app/src/main/res/layouts/row_layouts/layout/app_details_header.xml new file mode 100644 index 0000000..ab03996 --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/app_details_header.xml @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layouts/row_layouts/layout/app_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/app_row_item.xml new file mode 100644 index 0000000..7274d1e --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/app_row_item.xml @@ -0,0 +1,24 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/floating_apps_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/floating_apps_row_item.xml new file mode 100644 index 0000000..1d692f0 --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/floating_apps_row_item.xml @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/folder_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/folder_row_item.xml new file mode 100644 index 0000000..765adee --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/folder_row_item.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/icon_pack_layout.xml b/app/src/main/res/layouts/row_layouts/layout/icon_pack_layout.xml new file mode 100644 index 0000000..bbffafa --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/icon_pack_layout.xml @@ -0,0 +1,41 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/permission_row_item.xml b/app/src/main/res/layouts/row_layouts/layout/permission_row_item.xml new file mode 100644 index 0000000..1981387 --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/permission_row_item.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layouts/row_layouts/layout/select_apps_folder_row.xml b/app/src/main/res/layouts/row_layouts/layout/select_apps_folder_row.xml new file mode 100644 index 0000000..66c9515 --- /dev/null +++ b/app/src/main/res/layouts/row_layouts/layout/select_apps_folder_row.xml @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/add_menu.xml b/app/src/main/res/menu/add_menu.xml new file mode 100644 index 0000000..a819672 --- /dev/null +++ b/app/src/main/res/menu/add_menu.xml @@ -0,0 +1,17 @@ + +

+ + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml new file mode 100644 index 0000000..a5a6cbb --- /dev/null +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml new file mode 100644 index 0000000..52323e2 --- /dev/null +++ b/app/src/main/res/menu/drawer_menu.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/remove_menu.xml b/app/src/main/res/menu/remove_menu.xml new file mode 100644 index 0000000..42b488a --- /dev/null +++ b/app/src/main/res/menu/remove_menu.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..0e9115e Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..ca5dc0b Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..ea4da7f Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..ca1e73f Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..6e82da1 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml new file mode 100755 index 0000000..c44f4de --- /dev/null +++ b/app/src/main/res/values-land/dimens.xml @@ -0,0 +1,6 @@ + + 46dp + 5 + 140dp + + diff --git a/app/src/main/res/values-sw600dp-land/dimens.xml b/app/src/main/res/values-sw600dp-land/dimens.xml new file mode 100644 index 0000000..5e6d98e --- /dev/null +++ b/app/src/main/res/values-sw600dp-land/dimens.xml @@ -0,0 +1,7 @@ + + + 52dp + 6 + 24dp + 2 + \ No newline at end of file diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml new file mode 100644 index 0000000..e1a3b2b --- /dev/null +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -0,0 +1,7 @@ + + + 32dp + 5 + 24dp + 1 + \ No newline at end of file diff --git a/app/src/main/res/values-sw720dp-land/dimens.xml b/app/src/main/res/values-sw720dp-land/dimens.xml new file mode 100644 index 0000000..251c3b2 --- /dev/null +++ b/app/src/main/res/values-sw720dp-land/dimens.xml @@ -0,0 +1,7 @@ + + + 82dp + 6 + 24dp + 2 + \ No newline at end of file diff --git a/app/src/main/res/values-sw720dp/dimens.xml b/app/src/main/res/values-sw720dp/dimens.xml new file mode 100644 index 0000000..3c35b7c --- /dev/null +++ b/app/src/main/res/values-sw720dp/dimens.xml @@ -0,0 +1,11 @@ + + + 58dp + 5 + 24dp + 1 + 320dp + 56dp + 72dp + 96dp + \ No newline at end of file diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml new file mode 100644 index 0000000..862bf83 --- /dev/null +++ b/app/src/main/res/values-v21/styles.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-w820dp-land/dimens.xml b/app/src/main/res/values-w820dp-land/dimens.xml new file mode 100755 index 0000000..29515b7 --- /dev/null +++ b/app/src/main/res/values-w820dp-land/dimens.xml @@ -0,0 +1,6 @@ + + 84dp + 7 + 24dp + 3 + diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml new file mode 100755 index 0000000..9c8d2bf --- /dev/null +++ b/app/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + 64dp + 6 + 24dp + 2 + diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..dc5345b --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,43 @@ + + + + + Apps + Folders + + + + Small + Medium + Large + + + + #FF2A456B + #213755 + #ff3f51b5 + #ff9e9e9e + #ff424242 + #ff1c1c1c + #ff000000 + #ff607d8b + #ff37474f + #ff795548 + #ffd32f2f + #ffe91e63 + #ff9c27b0 + #ff5e35b1 + #ff1976d2 + #ff03a9f4 + #ff00bcd4 + #ff009688 + #ff43a047 + #ff7cb342 + #ffc0ca33 + #fffbc02d + #ffffb300 + #fffb8c00 + #fff4511e + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..b76d2cb --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..2ee1c62 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,19 @@ + + + #FF2A456B + #213755 + #C5CAE9 + #448AFF + #212121 + #727272 + @color/white + #B6B6B6 + #00000000 + #ff000000 + #ffffff + #80000000 + #ffF5F5F5 + #ECEFF1 + #CFD8DC + #111C2C + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..bd51310 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ + + 16dp + 4dp + 8dp + 16dp + 24dp + 32dp + 48dp + 64dp + 56dp + 72dp + 16dp + 60dp + 4 + 1 + 64dp + 280dp + 48dp + 56dp + 72dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..1b2f723 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,136 @@ + + Fast Access + com.fastaccess.ui.widgets.recyclerview.layout_manager.LinearManager + com.fastaccess.ui.widgets.recyclerview.layout_manager.GridManager + com.fastaccess.ui.widgets.recyclerview.layout_manager.StaggeredManager + @string/appbar_scrolling_view_behavior + Skip + In Progress, Please Wait… + Close + Action + Settings + Are you sure? + Cancel + Reload + No Data. + Search + No Applications + Permission is needed so Fast Access can function at its best. Please allow the permission manually from the settings screen. + Open Settings Screen. + Terminate App + This permission is needed to save your backups into the SDCARD + We need this permission to be able to store your backups into your sdcard. + Write To SDCARD Permission + This permission is needed to be able to restore your backups. + We need this permission to be able to restore your backups from your sdcard. + Read From SDCARD Permission + Clear + Select All + Clear All + Uninstall + Share + Menu + General Settings + Application Version + Open Source Libraries + Check FA source code + Backup apps with their data. + Cancelling, please wait… + Your fav apps are always a click away. + Get Theme Packs + No Theme Packs Found. + Choose Theme Pack + Default + Icon Pack is not supported + My Apps + Device Apps + Selected + Edit + Add Apps + No folders yet, click on + button to start adding folders + Save + Folder Name + Required + Add + Apps + Delete + Click here to change color + Select Apps + Add All + Done + Remove All + Click to open FA + Click here to start FA service + Choose Background + About + Background color of FA apps list. + FA Apps Background + FA Floating Mode + Select FA floating mode. + Auto Save Last Position + Auto save last position of FA floating icon. + Sticky FA Floating Icon + Keep FA floating icon attached to screen edges. + Keep FA floating apps always showing. + FA Floating App Visibility + Make the status bar icon transparent + Status Bar Icon + Make FA floating icon semi-transparent when is not touched. + Semi-Transparent FA Floating Icon + Change the transparency value of FA Floating icon. + FA Floating Icon Transparency + Adjust FA apps background transparency + FA App Background Transparency + Choose icons theme pack. + Icons Theme Pack + Choose custom icon to use as FA floating icon + Custom FA Icon + FA Apps Customization + FA Icon Customization + Predefined FA Floating Icon Size + Choose from predefined icon sizes. + Choose Icon Size + Custom FA Floating Icon Size + Manually choose the size that fits you. + Choose Icon Gap + Choose from predefined icon gaps. + FA Floating Icons Gap + Auto Start FA + Auto start FA when device is rebooted. + FA Orientation Mode + Choose FA orientation mode to display horizontally. + Requires FA service to be restarted.\nLongPress FA icon to restart from notification bar. + Change Transparency + Value + Change Icon Size + Folders + Start Service + Stop Service + Please enable FA as floating window to be able to use FA. + Back + App Permissions Details + From Icon Pack + From Gallery + Icon Pack does not support sending icons to other apps, please use different icon pack. + Choose Icon + Crop Image. + No Apps Selected, Select ones from Device Apps Tab. + FA Folder Mode Not Available\ + FA Folder Mode + FA App Mode + Across Devices Backup & Restore + Backup + Restore + + Sign-In with Google+ + Failed Sign-In + + You must Sign-In first + Backup in progress, Please wai… + Successfully Restored, App restart is required + Restore in progress, Please wait… + No data to restore, please backup first. + Ok + This will overwrite any existing backup you have made so far.\nAre you sure? + Restoring will overwrite all your settings and data within the app.\nAre you sure? + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..44842cd --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/fa_settings.xml b/app/src/main/res/xml/fa_settings.xml new file mode 100644 index 0000000..95b5b0c --- /dev/null +++ b/app/src/main/res/xml/fa_settings.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/fa_shortcuts.xml b/app/src/main/res/xml/fa_shortcuts.xml new file mode 100644 index 0000000..fac95ca --- /dev/null +++ b/app/src/main/res/xml/fa_shortcuts.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..15ea4dd --- /dev/null +++ b/build.gradle @@ -0,0 +1,24 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' + classpath 'com.google.gms:google-services:3.0.0' + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + jcenter() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/color-picker/.gitignore b/color-picker/.gitignore new file mode 100755 index 0000000..796b96d --- /dev/null +++ b/color-picker/.gitignore @@ -0,0 +1 @@ +/build diff --git a/color-picker/build.gradle b/color-picker/build.gradle new file mode 100755 index 0000000..0c87afb --- /dev/null +++ b/color-picker/build.gradle @@ -0,0 +1,28 @@ +apply plugin: 'com.android.library' + + +android { + compileSdkVersion 25 + buildToolsVersion "24.0.2" + + defaultConfig { + minSdkVersion 18 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + ext { supportVerion = "25.0.0" } + compile "com.android.support:appcompat-v7:${supportVerion}" + compile "com.android.support:design:${supportVerion}" + compile "com.android.support:preference-v14:${supportVerion}" +} \ No newline at end of file diff --git a/color-picker/proguard-rules.pro b/color-picker/proguard-rules.pro new file mode 100755 index 0000000..7928f2a --- /dev/null +++ b/color-picker/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/ty/Android/Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# 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 *; +#} diff --git a/color-picker/src/main/AndroidManifest.xml b/color-picker/src/main/AndroidManifest.xml new file mode 100755 index 0000000..ddfefa3 --- /dev/null +++ b/color-picker/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/color-picker/src/main/java/org/xdty/preference/AppCompatColorPreference.java b/color-picker/src/main/java/org/xdty/preference/AppCompatColorPreference.java new file mode 100755 index 0000000..4dce613 --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/AppCompatColorPreference.java @@ -0,0 +1,283 @@ +package org.xdty.preference; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.OvalShape; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.PreferenceManager; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.xdty.preference.colorpicker.ColorPickerDialog; +import org.xdty.preference.colorpicker.R; + +/** + * A preference showing a {@link ColorPickerDialog} to allow the user to select a color to save as {@link Preference}. + */ +@SuppressWarnings("JavaDoc") public class AppCompatColorPreference extends Preference implements SharedPreferences.OnSharedPreferenceChangeListener { + + private static final int DEFAULT_VALUE = Color.BLACK; + + private int mTitle = R.string.color_picker_default_title; + private int mCurrentValue; + private int[] mColors; + private int mColumns; + private boolean mMaterial; + private View mColorView; + + public AppCompatColorPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable + .ColorPreference, 0, 0); + try { + int id = a.getResourceId(R.styleable.ColorPreference_colorDialogColors, R.array.default_rainbow); + if (id != 0) { + mColors = getContext().getResources().getIntArray(id); + } + id = a.getResourceId(R.styleable.ColorPreference_colorDialogTitle, 0); + if (id != 0) { + mTitle = a.getResourceId(R.styleable.ColorPreference_colorDialogTitle, R.string.color_picker_default_title); + } + mColumns = a.getInt(R.styleable.ColorPreference_colorDialogColumns, 5); + mMaterial = a.getBoolean(R.styleable.ColorPreference_colorDialogMaterial, true); + } finally { + a.recycle(); + } + } + + @Override protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInteger(index, DEFAULT_VALUE); + } + + @Override public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + View view = holder.itemView; + mColorView = new View(getContext()); + mColorView.setId("ID".hashCode()); + int size = (int) dpToPx(32); + mColorView.setLayoutParams(new ViewGroup.LayoutParams(size, size)); + ViewGroup frame = (ViewGroup) view.findViewById(android.R.id.widget_frame); + frame.setVisibility(View.VISIBLE); + addView(frame, mColorView); + if (mMaterial) { + TextView textTitle = (TextView) view.findViewById(android.R.id.title); + TextView textSummary = (TextView) view.findViewById(android.R.id.summary); + textTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + textSummary.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); + textTitle.setTextColor(getColor(android.R.attr.textColor)); + textSummary.setTextColor(getColor(android.R.attr.textColorSecondary)); + View parent = (View) textSummary.getParent().getParent(); + parent.setPadding((int) dpToPx(16), 0, (int) dpToPx(16), 0); + } + } + + @Override protected void onClick() { + int[] colors = mColors.length != 0 ? mColors : new int[]{ + Color.BLACK, Color.WHITE, Color + .RED, Color.GREEN, Color.BLUE + }; + ColorPickerDialog colorPickerDialog = ColorPickerDialog.newInstance(mTitle, + colors, mCurrentValue, mColumns, + ColorPickerDialog.SIZE_SMALL, getKey()); + AppCompatActivity appCompatActivity = getActivity(getContext()); + if (appCompatActivity != null) colorPickerDialog.show(appCompatActivity.getSupportFragmentManager(), "ColorPickerDialog"); + } + + @Override public void onDetached() { + super.onDetached(); + PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this); + } + + @Override public void onAttached() { + super.onAttached(); + PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this); + } + + @Override protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { + if (restorePersistedValue) { + // Restore existing state + mCurrentValue = this.getPersistedInt(DEFAULT_VALUE); + } else { + // Set default state from the XML attribute + mCurrentValue = (Integer) defaultValue; + persistInt(mCurrentValue); + } + } + + @Override protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + // Check whether this Preference is persistent (continually saved) + if (isPersistent()) { + // No need to save instance state since it's persistent, + // use superclass state + return superState; + } + + // Create instance of custom BaseSavedState + final SavedState myState = new SavedState(superState); + // Set the state's value with the class member that holds current + // setting value + myState.current = mCurrentValue; + myState.colors = mColors; + myState.columns = mColumns; + return myState; + } + + @Override protected void onRestoreInstanceState(Parcelable state) { + // Check whether we saved the state in onSaveInstanceState + if (state == null || !state.getClass().equals(SavedState.class)) { + // Didn't save the state, so call superclass + super.onRestoreInstanceState(state); + return; + } + + // Cast state to custom BaseSavedState and pass to superclass + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + + // Update own values + mCurrentValue = myState.current; + mColors = myState.colors; + mColumns = myState.columns; + + // Update shown color + updateShownColor(); + + // Set this Preference's widget to reflect the restored state + //mNumberPicker.setValue(myState.value); + } + + /** + * hacky way to receive the changed color when activity is rotated. + * + * @param sharedPreferences + * @param key + */ + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Log.e(key, key); + if (getKey().equalsIgnoreCase(key)) { + mCurrentValue = sharedPreferences.getInt(key, mCurrentValue); + persistInt(mCurrentValue); + updateShownColor(); + } + } + + private void updateShownColor() { + if (mColorView == null) { + try { + notifyChanged(); + } catch (Exception ignored) {}//hack, avoid calling notify when recyclerview is manipulating views. + return; + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mColorView.setBackground(new ShapeDrawable(new OvalShape())); + ((ShapeDrawable) mColorView.getBackground()).getPaint().setColor(mCurrentValue); + } else { + mColorView.setBackground(new ColorCircleDrawable(mCurrentValue)); + } + mColorView.invalidate(); + } + + /** + * Convert a dp size to pixel. Useful for specifying view sizes in code. + * + * @param dp + * The size in density-independent pixels. + * @return {@code px} - The size in generic pixels (density-dependent). + */ + private float dpToPx(float dp) { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + getContext().getResources().getDisplayMetrics()); + } + + private int getColor(int attrId) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getContext().getTheme(); + theme.resolveAttribute(attrId, typedValue, true); + TypedArray arr = getContext().obtainStyledAttributes(typedValue.data, new int[]{attrId}); + int color = arr.getColor(0, -1); + arr.recycle(); + return color; + } + + @Nullable public static AppCompatActivity getActivity(@Nullable Context cont) { + if (cont == null) return null; + else if (cont instanceof AppCompatActivity) return (AppCompatActivity) cont; + else if (cont instanceof ContextWrapper) return getActivity(((ContextWrapper) cont).getBaseContext()); + return null; + } + + private static class SavedState extends BaseSavedState { + // Standard creator object using an instance of this class + public static final Creator CREATOR = + new Creator() { + + public AppCompatColorPreference.SavedState createFromParcel(Parcel in) { + return new AppCompatColorPreference.SavedState(in); + } + + public AppCompatColorPreference.SavedState[] newArray(int size) { + return new AppCompatColorPreference.SavedState[size]; + } + }; + // Member that holds the preference's values + int current; + int[] colors; + int columns; + + public SavedState(Parcelable superState) { + super(superState); + } + + public SavedState(Parcel source) { + super(source); + // Get the current preference's values + current = source.readInt(); + source.readIntArray(colors); + columns = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + // Write the preference's values + dest.writeInt(current); + dest.writeIntArray(colors); + dest.writeInt(columns); + } + } + + /** + * recycling the added view to avoid adding it multiple times. + * + * @param view + * @param toAdd + */ + private void addView(ViewGroup view, View toAdd) { + if (view != null && toAdd != null) { + if (toAdd.getParent() != null) { + ((ViewGroup) toAdd.getParent()).removeView(toAdd); + } + view.removeAllViews(); + view.addView(toAdd); + } + updateShownColor(); + } + +} \ No newline at end of file diff --git a/color-picker/src/main/java/org/xdty/preference/ColorCircleDrawable.java b/color-picker/src/main/java/org/xdty/preference/ColorCircleDrawable.java new file mode 100755 index 0000000..f374a35 --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/ColorCircleDrawable.java @@ -0,0 +1,46 @@ +package org.xdty.preference; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; + +public class ColorCircleDrawable extends Drawable { + private final Paint mPaint; + private int mRadius = 0; + + public ColorCircleDrawable(final int color) { + this.mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + this.mPaint.setColor(color); + } + + @Override + public void draw(@NonNull final Canvas canvas) { + final Rect bounds = getBounds(); + canvas.drawCircle(bounds.centerX(), bounds.centerY(), mRadius, mPaint); + } + + @Override + protected void onBoundsChange(final Rect bounds) { + super.onBoundsChange(bounds); + mRadius = Math.min(bounds.width(), bounds.height()) / 2; + } + + @Override + public void setAlpha(final int alpha) { + mPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(final ColorFilter cf) { + mPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } +} \ No newline at end of file diff --git a/color-picker/src/main/java/org/xdty/preference/ColorPreference.java b/color-picker/src/main/java/org/xdty/preference/ColorPreference.java new file mode 100755 index 0000000..0477063 --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/ColorPreference.java @@ -0,0 +1,236 @@ +package org.xdty.preference; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.ShapeDrawable; +import android.graphics.drawable.shapes.OvalShape; +import android.os.Build; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.Preference; +import android.support.v7.app.AppCompatActivity; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import org.xdty.preference.colorpicker.ColorPickerDialog; +import org.xdty.preference.colorpicker.ColorPickerSwatch; +import org.xdty.preference.colorpicker.R; + +/** + * A preference showing a {@link ColorPickerDialog} to allow the user to select a color to save as {@link Preference}. + */ +public class ColorPreference extends Preference implements ColorPickerSwatch + .OnColorSelectedListener { + + private static final int DEFAULT_VALUE = Color.BLACK; + + private int mTitle = R.string.color_picker_default_title; + private int mCurrentValue; + private int[] mColors; + private int mColumns; + private boolean mMaterial; + + private View mColorView; + + public ColorPreference(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ColorPreference, 0, 0); + try { + int id = a.getResourceId(R.styleable.ColorPreference_colorDialogColors, R.array.default_rainbow); + if (id != 0) { + mColors = getContext().getResources().getIntArray(id); + } + id = a.getResourceId(R.styleable.ColorPreference_colorDialogTitle, 0); + if (id != 0) { + mTitle = a.getResourceId(R.styleable.ColorPreference_colorDialogTitle, R.string.color_picker_default_title); + } + mColumns = a.getInt(R.styleable.ColorPreference_colorDialogColumns, 5); + mMaterial = a.getBoolean(R.styleable.ColorPreference_colorDialogMaterial, true); + } finally { + a.recycle(); + } + } + + @Override protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInteger(index, DEFAULT_VALUE); + } + + @Override protected View onCreateView(ViewGroup parent) { + View s = super.onCreateView(parent); + mColorView = new View(getContext()); + int size = (int) dpToPx(32); + mColorView.setLayoutParams(new ViewGroup.LayoutParams(size, size)); + updateShownColor(); + ViewGroup w = (ViewGroup) s.findViewById(android.R.id.widget_frame); + w.setVisibility(View.VISIBLE); + w.addView(mColorView); + return s; + } + + @Override protected void onBindView(View view) { + super.onBindView(view); + if (mMaterial) { + TextView textTitle = (TextView) view.findViewById(android.R.id.title); + TextView textSummary = (TextView) view.findViewById(android.R.id.summary); + + textTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16); + textSummary.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); + textTitle.setTextColor(getColor(android.R.attr.textColorPrimary)); + textSummary.setTextColor(getColor(android.R.attr.textColorSecondary)); + + View parent = (View) textSummary.getParent().getParent(); + parent.setPadding((int) dpToPx(16), 0, (int) dpToPx(16), 0); + } + } + + @Override protected void onClick() { + int[] colors = mColors.length != 0 ? mColors : new int[]{ + Color.BLACK, Color.WHITE, Color + .RED, Color.GREEN, Color.BLUE + }; + ColorPickerDialog d = ColorPickerDialog.newInstance(mTitle, + colors, mCurrentValue, mColumns, + ColorPickerDialog.SIZE_SMALL); + d.setOnColorSelectedListener(this); + d.show(((AppCompatActivity) getContext()).getSupportFragmentManager(), "ColorPickerDialog"); + } + + @Override protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { + if (restorePersistedValue) { + // Restore existing state + mCurrentValue = this.getPersistedInt(DEFAULT_VALUE); + } else { + // Set default state from the XML attribute + mCurrentValue = (Integer) defaultValue; + persistInt(mCurrentValue); + } + } + + @Override protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + // Check whether this Preference is persistent (continually saved) + if (isPersistent()) { + // No need to save instance state since it's persistent, + // use superclass state + return superState; + } + + // Create instance of custom BaseSavedState + final SavedState myState = new SavedState(superState); + // Set the state's value with the class member that holds current + // setting value + myState.current = mCurrentValue; + myState.colors = mColors; + myState.columns = mColumns; + return myState; + } + + @Override protected void onRestoreInstanceState(Parcelable state) { + // Check whether we saved the state in onSaveInstanceState + if (state == null || !state.getClass().equals(SavedState.class)) { + // Didn't save the state, so call superclass + super.onRestoreInstanceState(state); + return; + } + + // Cast state to custom BaseSavedState and pass to superclass + SavedState myState = (SavedState) state; + super.onRestoreInstanceState(myState.getSuperState()); + + // Update own values + mCurrentValue = myState.current; + mColors = myState.colors; + mColumns = myState.columns; + + // Update shown color + updateShownColor(); + + // Set this Preference's widget to reflect the restored state + //mNumberPicker.setValue(myState.value); + } + + @Override public void onColorSelected(int color) { + persistInt(color); + mCurrentValue = color; + updateShownColor(); + } + + private void updateShownColor() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mColorView.setBackground(new ShapeDrawable(new OvalShape())); + ((ShapeDrawable) mColorView.getBackground()).getPaint().setColor(mCurrentValue); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + mColorView.setBackground(new ColorCircleDrawable(mCurrentValue)); + } else { + mColorView.setBackground(new ColorCircleDrawable(mCurrentValue)); + } + mColorView.invalidate(); + } + + /** + * Convert a dp size to pixel. Useful for specifying view sizes in code. + * + * @param dp + * The size in density-independent pixels. + * @return {@code px} - The size in generic pixels (density-dependent). + */ + private float dpToPx(float dp) { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, + getContext().getResources().getDisplayMetrics()); + } + + private int getColor(int attrId) { + TypedValue typedValue = new TypedValue(); + Resources.Theme theme = getContext().getTheme(); + theme.resolveAttribute(attrId, typedValue, true); + TypedArray arr = getContext().obtainStyledAttributes(typedValue.data, new int[]{attrId}); + int color = arr.getColor(0, -1); + arr.recycle(); + return color; + } + + private static class SavedState extends BaseSavedState { + // Standard creator object using an instance of this class + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + // Member that holds the preference's values + int current; + int[] colors; + int columns; + + public SavedState(Parcelable superState) { + super(superState); + } + + public SavedState(Parcel source) { + super(source); + // Get the current preference's values + current = source.readInt(); + source.readIntArray(colors); + columns = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + // Write the preference's values + dest.writeInt(current); + dest.writeIntArray(colors); + dest.writeInt(columns); + } + } +} \ No newline at end of file diff --git a/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerDialog.java b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerDialog.java new file mode 100755 index 0000000..7b55cc6 --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerDialog.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.xdty.preference.colorpicker; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.support.v7.preference.PreferenceManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ProgressBar; + +import org.xdty.preference.colorpicker.ColorPickerSwatch.OnColorSelectedListener; + + +/** + * A dialog which takes in as input an array of colors and creates a palette allowing the user to select a specific color swatch, which invokes a + * listener. + */ +public class ColorPickerDialog extends DialogFragment implements OnColorSelectedListener { + + public static final int SIZE_LARGE = 1; + public static final int SIZE_SMALL = 2; + + protected AlertDialog mAlertDialog; + + protected static final String KEY_TITLE_ID = "title_id"; + protected static final String KEY_COLORS = "colors"; + protected static final String KEY_COLOR_CONTENT_DESCRIPTIONS = "color_content_descriptions"; + protected static final String KEY_SELECTED_COLOR = "selected_color"; + protected static final String KEY_COLUMNS = "columns"; + protected static final String KEY_SIZE = "size"; + + protected int mTitleResId = R.string.color_picker_default_title; + protected int[] mColors = null; + protected String[] mColorContentDescriptions = null; + protected int mSelectedColor; + protected int mColumns; + protected int mSize; + protected String key; + private ColorPickerPalette mPalette; + private ProgressBar mProgress; + + protected OnColorSelectedListener mListener; + + public static ColorPickerDialog newInstance(int titleResId, int[] colors, int selectedColor, + int columns, int size) { + ColorPickerDialog ret = new ColorPickerDialog(); + ret.initialize(titleResId, colors, selectedColor, columns, size); + return ret; + } + + @Override public void onAttach(Context context) { + super.onAttach(context); + Log.e("ColorPickerPalette", context + " " + getParentFragment()); + if (getParentFragment() != null && (getParentFragment() instanceof OnColorSelectedListener)) { + mListener = (OnColorSelectedListener) getParentFragment(); + } else if (context instanceof OnColorSelectedListener) { + mListener = (OnColorSelectedListener) context; + } + } + + @Override public void onDetach() { + super.onDetach(); + mListener = null; + } + + @Override public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putIntArray(KEY_COLORS, mColors); + outState.putInt(KEY_SELECTED_COLOR, mSelectedColor); + outState.putStringArray(KEY_COLOR_CONTENT_DESCRIPTIONS, mColorContentDescriptions); + outState.putString("key", key); + } + + @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mTitleResId = getArguments().getInt(KEY_TITLE_ID); + mColumns = getArguments().getInt(KEY_COLUMNS); + mSize = getArguments().getInt(KEY_SIZE); + key = getArguments().getString("key"); + } + if (savedInstanceState != null) { + mColors = savedInstanceState.getIntArray(KEY_COLORS); + mSelectedColor = savedInstanceState.getInt(KEY_SELECTED_COLOR); + mColorContentDescriptions = savedInstanceState.getStringArray( + KEY_COLOR_CONTENT_DESCRIPTIONS); + key = savedInstanceState.getString("key"); + } + } + + @SuppressLint("InflateParams") @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + View view = LayoutInflater.from(getActivity()).inflate(R.layout.color_picker_dialog, null, false); + final Activity activity = getActivity(); + mProgress = (ProgressBar) view.findViewById(android.R.id.progress); + mPalette = (ColorPickerPalette) view.findViewById(R.id.color_picker); + mPalette.init(mSize, mColumns, this); + + if (mColors != null) { + showPaletteView(); + } + + mAlertDialog = new AlertDialog.Builder(activity) + .setTitle(mTitleResId) + .setView(view) + .create(); + mAlertDialog.setCanceledOnTouchOutside(true); + return mAlertDialog; + } + + @Override public void onColorSelected(int color) { + if (mListener != null) { + mListener.onColorSelected(color); + } + PreferenceManager.getDefaultSharedPreferences(getContext()) + .edit().putInt(key, color).apply(); + if (getTargetFragment() instanceof OnColorSelectedListener) { + final OnColorSelectedListener listener = (OnColorSelectedListener) getTargetFragment(); + listener.onColorSelected(color); + } + if (color != mSelectedColor) { + mSelectedColor = color; + mPalette.drawPalette(mColors, mSelectedColor); + } + dismiss(); + } + + public void initialize(int titleResId, int[] colors, int selectedColor, int columns, int size) { + setArguments(titleResId, columns, size); + setColors(colors, selectedColor); + } + + public void setArguments(int titleResId, int columns, int size) { + Bundle bundle = new Bundle(); + bundle.putInt(KEY_TITLE_ID, titleResId); + bundle.putInt(KEY_COLUMNS, columns); + bundle.putInt(KEY_SIZE, size); + setArguments(bundle); + } + + public void setOnColorSelectedListener(OnColorSelectedListener listener) { + mListener = listener; + } + + public void showPaletteView() { + if (mProgress != null && mPalette != null) { + mProgress.setVisibility(View.GONE); + refreshPalette(); + mPalette.setVisibility(View.VISIBLE); + } + } + + public void showProgressBarView() { + if (mProgress != null && mPalette != null) { + mProgress.setVisibility(View.VISIBLE); + mPalette.setVisibility(View.GONE); + } + } + + public void setColors(int[] colors, int selectedColor) { + if (mColors != colors || mSelectedColor != selectedColor) { + mColors = colors; + mSelectedColor = selectedColor; + refreshPalette(); + } + } + + public void setColors(int[] colors) { + if (mColors != colors) { + mColors = colors; + refreshPalette(); + } + } + + public void setSelectedColor(int color) { + if (mSelectedColor != color) { + mSelectedColor = color; + refreshPalette(); + } + } + + public void setColorContentDescriptions(String[] colorContentDescriptions) { + if (mColorContentDescriptions != colorContentDescriptions) { + mColorContentDescriptions = colorContentDescriptions; + refreshPalette(); + } + } + + private void refreshPalette() { + if (mPalette != null && mColors != null) { + mPalette.drawPalette(mColors, mSelectedColor, mColorContentDescriptions); + } + } + + public int[] getColors() { + return mColors; + } + + public int getSelectedColor() { + return mSelectedColor; + } + + public static ColorPickerDialog newInstance(int titleResId, int[] colors, int selectedColor, + int columns, int size, String key) { + ColorPickerDialog ret = new ColorPickerDialog(); + ret.initialize(titleResId, colors, selectedColor, columns, size, key); + return ret; + } + + private void initialize(int titleResId, int[] colors, int selectedColor, int columns, int size, String key) { + setArguments(titleResId, columns, size, key); + setColors(colors, selectedColor); + } + + private void setArguments(int titleResId, int columns, int size, String key) { + Bundle bundle = new Bundle(); + bundle.putInt(KEY_TITLE_ID, titleResId); + bundle.putInt(KEY_COLUMNS, columns); + bundle.putInt(KEY_SIZE, size); + bundle.putString("key", key); + setArguments(bundle); + } +} diff --git a/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerPalette.java b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerPalette.java new file mode 100755 index 0000000..bf7f39b --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerPalette.java @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.xdty.preference.colorpicker; + +import android.content.Context; +import android.content.res.Resources; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TableLayout; +import android.widget.TableRow; + +import org.xdty.preference.colorpicker.ColorPickerSwatch.OnColorSelectedListener; + + +/** + * A color picker custom view which creates an grid of color squares. The number of squares per + * row (and the padding between the squares) is determined by the user. + */ +public class ColorPickerPalette extends TableLayout { + + public OnColorSelectedListener mOnColorSelectedListener; + + private String mDescription; + private String mDescriptionSelected; + + private int mSwatchLength; + private int mMarginSize; + private int mNumColumns; + + public ColorPickerPalette(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ColorPickerPalette(Context context) { + super(context); + } + + /** + * Initialize the size, columns, and listener. Size should be a pre-defined size (SIZE_LARGE + * or SIZE_SMALL) from ColorPickerDialogFragment. + */ + public void init(int size, int columns, OnColorSelectedListener listener) { + mNumColumns = columns; + Resources res = getResources(); + if (size == ColorPickerDialog.SIZE_LARGE) { + mSwatchLength = res.getDimensionPixelSize(R.dimen.color_swatch_large); + mMarginSize = res.getDimensionPixelSize(R.dimen.color_swatch_margins_large); + } else { + mSwatchLength = res.getDimensionPixelSize(R.dimen.color_swatch_small); + mMarginSize = res.getDimensionPixelSize(R.dimen.color_swatch_margins_small); + } + mOnColorSelectedListener = listener; + + mDescription = res.getString(R.string.color_swatch_description); + mDescriptionSelected = res.getString(R.string.color_swatch_description_selected); + } + + private TableRow createTableRow() { + TableRow row = new TableRow(getContext()); + ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT); + row.setLayoutParams(params); + return row; + } + + /** + * Adds swatches to table in a serpentine format. + */ + public void drawPalette(int[] colors, int selectedColor) { + drawPalette(colors, selectedColor, null); + } + + /** + * Adds swatches to table in a serpentine format. + */ + public void drawPalette(int[] colors, int selectedColor, String[] colorContentDescriptions) { + if (colors == null) { + return; + } + + this.removeAllViews(); + int tableElements = 0; + int rowElements = 0; + int rowNumber = 0; + + // Fills the table with swatches based on the array of colors. + TableRow row = createTableRow(); + for (int color : colors) { + View colorSwatch = createColorSwatch(color, selectedColor); + setSwatchDescription(rowNumber, tableElements, rowElements, color == selectedColor, + colorSwatch, colorContentDescriptions); + addSwatchToRow(row, colorSwatch, rowNumber); + + tableElements++; + rowElements++; + if (rowElements == mNumColumns) { + addView(row); + row = createTableRow(); + rowElements = 0; + rowNumber++; + } + } + + // Create blank views to fill the row if the last row has not been filled. + if (rowElements > 0) { + while (rowElements != mNumColumns) { + addSwatchToRow(row, createBlankSpace(), rowNumber); + rowElements++; + } + addView(row); + } + } + + /** + * Appends a swatch to the end of the row for even-numbered rows (starting with row 0), + * to the beginning of a row for odd-numbered rows. + */ + private static void addSwatchToRow(TableRow row, View swatch, int rowNumber) { + if (rowNumber % 2 == 0) { + row.addView(swatch); + } else { + row.addView(swatch, 0); + } + } + + /** + * Add a content description to the specified swatch view. Because the colors get added in a + * snaking form, every other row will need to compensate for the fact that the colors are added + * in an opposite direction from their left->right/top->bottom order, which is how the system + * will arrange them for accessibility purposes. + */ + private void setSwatchDescription(int rowNumber, int index, int rowElements, boolean selected, + View swatch, String[] contentDescriptions) { + String description; + if (contentDescriptions != null && contentDescriptions.length > index) { + description = contentDescriptions[index]; + } else { + int accessibilityIndex; + if (rowNumber % 2 == 0) { + // We're in a regular-ordered row + accessibilityIndex = index + 1; + } else { + // We're in a backwards-ordered row. + int rowMax = ((rowNumber + 1) * mNumColumns); + accessibilityIndex = rowMax - rowElements; + } + + if (selected) { + description = String.format(mDescriptionSelected, accessibilityIndex); + } else { + description = String.format(mDescription, accessibilityIndex); + } + } + swatch.setContentDescription(description); + } + + /** + * Creates a blank space to fill the row. + */ + private ImageView createBlankSpace() { + ImageView view = new ImageView(getContext()); + TableRow.LayoutParams params = new TableRow.LayoutParams(mSwatchLength, mSwatchLength); + params.setMargins(mMarginSize, mMarginSize, mMarginSize, mMarginSize); + view.setLayoutParams(params); + return view; + } + + /** + * Creates a color swatch. + */ + private ColorPickerSwatch createColorSwatch(int color, int selectedColor) { + ColorPickerSwatch view = new ColorPickerSwatch(getContext(), color, + color == selectedColor, mOnColorSelectedListener); + TableRow.LayoutParams params = new TableRow.LayoutParams(mSwatchLength, mSwatchLength); + params.setMargins(mMarginSize, mMarginSize, mMarginSize, mMarginSize); + view.setLayoutParams(params); + return view; + } +} diff --git a/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerSwatch.java b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerSwatch.java new file mode 100755 index 0000000..cc1ad4b --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorPickerSwatch.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.xdty.preference.colorpicker; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; + +/** + * Creates a circular swatch of a specified color. Adds a checkmark if marked as checked. + */ +public class ColorPickerSwatch extends FrameLayout implements View.OnClickListener { + private int mColor; + private ImageView mSwatchImage; + private ImageView mCheckmarkImage; + private OnColorSelectedListener mOnColorSelectedListener; + + /** + * Interface for a callback when a color square is selected. + */ + public interface OnColorSelectedListener { + + /** + * Called when a specific color square has been selected. + */ + void onColorSelected(int color); + } + + public ColorPickerSwatch(Context context, int color, boolean checked, OnColorSelectedListener listener) { + super(context); + mColor = color; + mOnColorSelectedListener = listener; + LayoutInflater.from(context).inflate(R.layout.color_picker_swatch, this); + mSwatchImage = (ImageView) findViewById(R.id.color_picker_swatch); + mCheckmarkImage = (ImageView) findViewById(R.id.color_picker_checkmark); + setColor(color); + setChecked(checked); + setOnClickListener(this); + } + + protected void setColor(int color) { + Drawable[] colorDrawable = new Drawable[]{ContextCompat.getDrawable(getContext(), R.drawable.color_picker_swatch)}; + mSwatchImage.setImageDrawable(new ColorStateDrawable(colorDrawable, color)); + } + + private void setChecked(boolean checked) { + if (checked) { + mCheckmarkImage.setVisibility(View.VISIBLE); + } else { + mCheckmarkImage.setVisibility(View.GONE); + } + } + + @Override public void onClick(View v) { + if (mOnColorSelectedListener != null) { + mOnColorSelectedListener.onColorSelected(mColor); + } + } +} diff --git a/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorStateDrawable.java b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorStateDrawable.java new file mode 100755 index 0000000..009a6e6 --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/colorpicker/ColorStateDrawable.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.xdty.preference.colorpicker; + +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; + +/** + * A drawable which sets its color filter to a color specified by the user, and changes to a + * slightly darker color when pressed or focused. + */ +public class ColorStateDrawable extends LayerDrawable { + + private static final float PRESSED_STATE_MULTIPLIER = 0.70f; + + private int mColor; + + public ColorStateDrawable(Drawable[] layers, int color) { + super(layers); + mColor = color; + } + + @Override + protected boolean onStateChange(int[] states) { + boolean pressedOrFocused = false; + for (int state : states) { + if (state == android.R.attr.state_pressed || state == android.R.attr.state_focused) { + pressedOrFocused = true; + break; + } + } + + if (pressedOrFocused) { + super.setColorFilter(getPressedColor(mColor), PorterDuff.Mode.SRC_ATOP); + } else { + super.setColorFilter(mColor, PorterDuff.Mode.SRC_ATOP); + } + + return super.onStateChange(states); + } + + /** + * Given a particular color, adjusts its value by a multiplier. + */ + private static int getPressedColor(int color) { + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + hsv[2] = hsv[2] * PRESSED_STATE_MULTIPLIER; + return Color.HSVToColor(hsv); + } + + @Override + public boolean isStateful() { + return true; + } +} diff --git a/color-picker/src/main/java/org/xdty/preference/colorpicker/HsvColorComparator.java b/color-picker/src/main/java/org/xdty/preference/colorpicker/HsvColorComparator.java new file mode 100755 index 0000000..9887244 --- /dev/null +++ b/color-picker/src/main/java/org/xdty/preference/colorpicker/HsvColorComparator.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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 org.xdty.preference.colorpicker; + +import android.graphics.Color; + +import java.util.Comparator; + +/** + * A color comparator which compares based on hue, saturation, and value. + */ +public class HsvColorComparator implements Comparator { + + @Override + public int compare(Integer lhs, Integer rhs) { + float[] hsv = new float[3]; + Color.colorToHSV(lhs, hsv); + float hue1 = hsv[0]; + float sat1 = hsv[1]; + float val1 = hsv[2]; + + float[] hsv2 = new float[3]; + Color.colorToHSV(rhs, hsv2); + float hue2 = hsv2[0]; + float sat2 = hsv2[1]; + float val2 = hsv2[2]; + + if (hue1 < hue2) { + return 1; + } else if (hue1 > hue2) { + return -1; + } else { + if (sat1 < sat2) { + return 1; + } else if (sat1 > sat2) { + return -1; + } else { + if (val1 < val2) { + return 1; + } else if (val1 > val2) { + return -1; + } + } + } + return 0; + } +} diff --git a/color-picker/src/main/res/drawable-hdpi/ic_colorpicker_swatch_selected.png b/color-picker/src/main/res/drawable-hdpi/ic_colorpicker_swatch_selected.png new file mode 100755 index 0000000..3cbfe1a Binary files /dev/null and b/color-picker/src/main/res/drawable-hdpi/ic_colorpicker_swatch_selected.png differ diff --git a/color-picker/src/main/res/drawable-mdpi/ic_colorpicker_swatch_selected.png b/color-picker/src/main/res/drawable-mdpi/ic_colorpicker_swatch_selected.png new file mode 100755 index 0000000..acbdeca Binary files /dev/null and b/color-picker/src/main/res/drawable-mdpi/ic_colorpicker_swatch_selected.png differ diff --git a/color-picker/src/main/res/drawable-xhdpi/ic_colorpicker_swatch_selected.png b/color-picker/src/main/res/drawable-xhdpi/ic_colorpicker_swatch_selected.png new file mode 100755 index 0000000..812ff2c Binary files /dev/null and b/color-picker/src/main/res/drawable-xhdpi/ic_colorpicker_swatch_selected.png differ diff --git a/color-picker/src/main/res/drawable/color_picker_swatch.xml b/color-picker/src/main/res/drawable/color_picker_swatch.xml new file mode 100755 index 0000000..db71091 --- /dev/null +++ b/color-picker/src/main/res/drawable/color_picker_swatch.xml @@ -0,0 +1,16 @@ + + + \ No newline at end of file diff --git a/color-picker/src/main/res/layout/color_picker_dialog.xml b/color-picker/src/main/res/layout/color_picker_dialog.xml new file mode 100755 index 0000000..a4f1780 --- /dev/null +++ b/color-picker/src/main/res/layout/color_picker_dialog.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/color-picker/src/main/res/layout/color_picker_swatch.xml b/color-picker/src/main/res/layout/color_picker_swatch.xml new file mode 100755 index 0000000..5afa4a5 --- /dev/null +++ b/color-picker/src/main/res/layout/color_picker_swatch.xml @@ -0,0 +1,32 @@ + + + + + + + + \ No newline at end of file diff --git a/color-picker/src/main/res/values-af/strings.xml b/color-picker/src/main/res/values-af/strings.xml new file mode 100755 index 0000000..2622bcd --- /dev/null +++ b/color-picker/src/main/res/values-af/strings.xml @@ -0,0 +1,22 @@ + + + + + "Kies \'n kleur" + "Kleur %1$d" + "Kleur %1$d gekies" + diff --git a/color-picker/src/main/res/values-am/strings.xml b/color-picker/src/main/res/values-am/strings.xml new file mode 100755 index 0000000..47bd2b8 --- /dev/null +++ b/color-picker/src/main/res/values-am/strings.xml @@ -0,0 +1,22 @@ + + + + + "ቀለም ምረጥ" + "ቀለም %1$d" + "ቀለም %1$d ተመርጧል" + diff --git a/color-picker/src/main/res/values-ar/strings.xml b/color-picker/src/main/res/values-ar/strings.xml new file mode 100755 index 0000000..e619ee4 --- /dev/null +++ b/color-picker/src/main/res/values-ar/strings.xml @@ -0,0 +1,22 @@ + + + + + "تحديد لون:" + "اللون %1$d" + "تم تحديد اللون %1$d" + diff --git a/color-picker/src/main/res/values-bg/strings.xml b/color-picker/src/main/res/values-bg/strings.xml new file mode 100755 index 0000000..979f2d6 --- /dev/null +++ b/color-picker/src/main/res/values-bg/strings.xml @@ -0,0 +1,22 @@ + + + + + "Избиране на цвят" + "Цвят %1$d" + "Избрахте цвят %1$d" + diff --git a/color-picker/src/main/res/values-bn-rBD/strings.xml b/color-picker/src/main/res/values-bn-rBD/strings.xml new file mode 100755 index 0000000..cd4a26d --- /dev/null +++ b/color-picker/src/main/res/values-bn-rBD/strings.xml @@ -0,0 +1,22 @@ + + + + + "একটি রঙ নির্বাচন করুন" + "রঙ %1$d" + "%1$d রঙ নির্বাচন করা হয়েছে" + diff --git a/color-picker/src/main/res/values-ca/strings.xml b/color-picker/src/main/res/values-ca/strings.xml new file mode 100755 index 0000000..613b935 --- /dev/null +++ b/color-picker/src/main/res/values-ca/strings.xml @@ -0,0 +1,22 @@ + + + + + "Selecciona un color" + "Color %1$d" + "S\'ha seleccionat el color %1$d" + diff --git a/color-picker/src/main/res/values-cs/strings.xml b/color-picker/src/main/res/values-cs/strings.xml new file mode 100755 index 0000000..08aaab8 --- /dev/null +++ b/color-picker/src/main/res/values-cs/strings.xml @@ -0,0 +1,22 @@ + + + + + "Vyberte barvu" + "Barva %1$d" + "Barva %1$d je vybrána" + diff --git a/color-picker/src/main/res/values-da/strings.xml b/color-picker/src/main/res/values-da/strings.xml new file mode 100755 index 0000000..ff3bfb5 --- /dev/null +++ b/color-picker/src/main/res/values-da/strings.xml @@ -0,0 +1,22 @@ + + + + + "Vælg en farve" + "Farve %1$d" + "Farve %1$d valgt" + diff --git a/color-picker/src/main/res/values-de/strings.xml b/color-picker/src/main/res/values-de/strings.xml new file mode 100755 index 0000000..f8ef0ba --- /dev/null +++ b/color-picker/src/main/res/values-de/strings.xml @@ -0,0 +1,22 @@ + + + + + "Farbe auswählen:" + "Farbe %1$d" + "Farbe %1$d ausgewählt" + diff --git a/color-picker/src/main/res/values-el/strings.xml b/color-picker/src/main/res/values-el/strings.xml new file mode 100755 index 0000000..d72feb0 --- /dev/null +++ b/color-picker/src/main/res/values-el/strings.xml @@ -0,0 +1,22 @@ + + + + + "Επιλέξτε χρώμα" + "Χρώμα %1$d" + "Επιλέχτηκε το χρώμα %1$d" + diff --git a/color-picker/src/main/res/values-en-rAU/strings.xml b/color-picker/src/main/res/values-en-rAU/strings.xml new file mode 100755 index 0000000..0d2fdc3 --- /dev/null +++ b/color-picker/src/main/res/values-en-rAU/strings.xml @@ -0,0 +1,22 @@ + + + + + "Select a Colour" + "Colour %1$d" + "Colour %1$d selected" + diff --git a/color-picker/src/main/res/values-en-rGB/strings.xml b/color-picker/src/main/res/values-en-rGB/strings.xml new file mode 100755 index 0000000..0d2fdc3 --- /dev/null +++ b/color-picker/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,22 @@ + + + + + "Select a Colour" + "Colour %1$d" + "Colour %1$d selected" + diff --git a/color-picker/src/main/res/values-en-rIN/strings.xml b/color-picker/src/main/res/values-en-rIN/strings.xml new file mode 100755 index 0000000..0d2fdc3 --- /dev/null +++ b/color-picker/src/main/res/values-en-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "Select a Colour" + "Colour %1$d" + "Colour %1$d selected" + diff --git a/color-picker/src/main/res/values-es-rUS/strings.xml b/color-picker/src/main/res/values-es-rUS/strings.xml new file mode 100755 index 0000000..c7ff625 --- /dev/null +++ b/color-picker/src/main/res/values-es-rUS/strings.xml @@ -0,0 +1,22 @@ + + + + + "Seleccionar un color" + "Color %1$d" + "Color %1$d seleccionado" + diff --git a/color-picker/src/main/res/values-es/strings.xml b/color-picker/src/main/res/values-es/strings.xml new file mode 100755 index 0000000..5a8db84 --- /dev/null +++ b/color-picker/src/main/res/values-es/strings.xml @@ -0,0 +1,22 @@ + + + + + "Selecciona un color" + "Color %1$d" + "Color %1$d seleccionado" + diff --git a/color-picker/src/main/res/values-et-rEE/strings.xml b/color-picker/src/main/res/values-et-rEE/strings.xml new file mode 100755 index 0000000..544e32c --- /dev/null +++ b/color-picker/src/main/res/values-et-rEE/strings.xml @@ -0,0 +1,22 @@ + + + + + "Valige värv" + "Värv %1$d" + "Valitud on värv %1$d" + diff --git a/color-picker/src/main/res/values-eu-rES/strings.xml b/color-picker/src/main/res/values-eu-rES/strings.xml new file mode 100755 index 0000000..ddb1966 --- /dev/null +++ b/color-picker/src/main/res/values-eu-rES/strings.xml @@ -0,0 +1,22 @@ + + + + + "Hautatu kolorea" + "%1$d" + "%1$d hautatu da" + diff --git a/color-picker/src/main/res/values-fa/strings.xml b/color-picker/src/main/res/values-fa/strings.xml new file mode 100755 index 0000000..ebc96f1 --- /dev/null +++ b/color-picker/src/main/res/values-fa/strings.xml @@ -0,0 +1,22 @@ + + + + + "انتخاب رنگ" + "رنگ %1$d" + "رنگ %1$d انتخاب شد" + diff --git a/color-picker/src/main/res/values-fi/strings.xml b/color-picker/src/main/res/values-fi/strings.xml new file mode 100755 index 0000000..5529866 --- /dev/null +++ b/color-picker/src/main/res/values-fi/strings.xml @@ -0,0 +1,22 @@ + + + + + "Valitse väri" + "Väri %1$d" + "Väri %1$d on valittu" + diff --git a/color-picker/src/main/res/values-fr-rCA/strings.xml b/color-picker/src/main/res/values-fr-rCA/strings.xml new file mode 100755 index 0000000..4a68b78 --- /dev/null +++ b/color-picker/src/main/res/values-fr-rCA/strings.xml @@ -0,0 +1,22 @@ + + + + + "Sélectionnez une couleur" + "Couleur %1$d" + "Couleur %1$d sélectionnée" + diff --git a/color-picker/src/main/res/values-fr/strings.xml b/color-picker/src/main/res/values-fr/strings.xml new file mode 100755 index 0000000..61ae1c3 --- /dev/null +++ b/color-picker/src/main/res/values-fr/strings.xml @@ -0,0 +1,22 @@ + + + + + "Sélectionner une couleur" + "Couleur %1$d" + "Couleur %1$d sélectionnée" + diff --git a/color-picker/src/main/res/values-gl-rES/strings.xml b/color-picker/src/main/res/values-gl-rES/strings.xml new file mode 100755 index 0000000..264956a --- /dev/null +++ b/color-picker/src/main/res/values-gl-rES/strings.xml @@ -0,0 +1,22 @@ + + + + + "Selecciona unha cor" + "Cor %1$d" + "Seleccionouse a cor %1$d" + diff --git a/color-picker/src/main/res/values-gu-rIN/strings.xml b/color-picker/src/main/res/values-gu-rIN/strings.xml new file mode 100755 index 0000000..5b7215a --- /dev/null +++ b/color-picker/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "એક રંગ પસંદ કરો" + "રંગ %1$d" + "રંગ %1$d પસંદ કર્યો" + diff --git a/color-picker/src/main/res/values-hi/strings.xml b/color-picker/src/main/res/values-hi/strings.xml new file mode 100755 index 0000000..7b27b66 --- /dev/null +++ b/color-picker/src/main/res/values-hi/strings.xml @@ -0,0 +1,22 @@ + + + + + "कोई रंग चुनें:" + "रंग %1$d" + "रंग %1$d चयनित" + diff --git a/color-picker/src/main/res/values-hr/strings.xml b/color-picker/src/main/res/values-hr/strings.xml new file mode 100755 index 0000000..2ad3fcf --- /dev/null +++ b/color-picker/src/main/res/values-hr/strings.xml @@ -0,0 +1,22 @@ + + + + + "Odabir boje" + "Boja %1$d" + "Odabrana je boja %1$d" + diff --git a/color-picker/src/main/res/values-hu/strings.xml b/color-picker/src/main/res/values-hu/strings.xml new file mode 100755 index 0000000..4e22944 --- /dev/null +++ b/color-picker/src/main/res/values-hu/strings.xml @@ -0,0 +1,22 @@ + + + + + "Válasszon színt" + "%1$d. szín" + "%1$d. szín kiválasztva" + diff --git a/color-picker/src/main/res/values-hy-rAM/strings.xml b/color-picker/src/main/res/values-hy-rAM/strings.xml new file mode 100755 index 0000000..5a63133 --- /dev/null +++ b/color-picker/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,22 @@ + + + + + "Ընտրեք գույնը" + "Գույնը %1$d" + "Ընտրվել է %1$d գույնը" + diff --git a/color-picker/src/main/res/values-in/strings.xml b/color-picker/src/main/res/values-in/strings.xml new file mode 100755 index 0000000..ef7d665 --- /dev/null +++ b/color-picker/src/main/res/values-in/strings.xml @@ -0,0 +1,22 @@ + + + + + "Pilih warna:" + "Warna %1$d" + "Warna %1$d dipilih" + diff --git a/color-picker/src/main/res/values-is-rIS/strings.xml b/color-picker/src/main/res/values-is-rIS/strings.xml new file mode 100755 index 0000000..1998b88 --- /dev/null +++ b/color-picker/src/main/res/values-is-rIS/strings.xml @@ -0,0 +1,22 @@ + + + + + "Veldu lit" + "Litur %1$d" + "Liturinn %1$d valinn" + diff --git a/color-picker/src/main/res/values-it/strings.xml b/color-picker/src/main/res/values-it/strings.xml new file mode 100755 index 0000000..8b899e4 --- /dev/null +++ b/color-picker/src/main/res/values-it/strings.xml @@ -0,0 +1,22 @@ + + + + + "Seleziona un colore" + "Colore %1$d" + "Colore selezionato: %1$d" + diff --git a/color-picker/src/main/res/values-iw/strings.xml b/color-picker/src/main/res/values-iw/strings.xml new file mode 100755 index 0000000..a4511f9 --- /dev/null +++ b/color-picker/src/main/res/values-iw/strings.xml @@ -0,0 +1,22 @@ + + + + + "בחר צבע" + "צבע %1$d" + "צבע %1$d נבחר" + diff --git a/color-picker/src/main/res/values-ja/strings.xml b/color-picker/src/main/res/values-ja/strings.xml new file mode 100755 index 0000000..7454447 --- /dev/null +++ b/color-picker/src/main/res/values-ja/strings.xml @@ -0,0 +1,22 @@ + + + + + "色の選択" + "色%1$d" + "色%1$dを選択しました" + diff --git a/color-picker/src/main/res/values-ka-rGE/strings.xml b/color-picker/src/main/res/values-ka-rGE/strings.xml new file mode 100755 index 0000000..594ba6d --- /dev/null +++ b/color-picker/src/main/res/values-ka-rGE/strings.xml @@ -0,0 +1,22 @@ + + + + + "ფერის არჩევა" + "ფერი %1$d" + "არჩეულია ფერი %1$d" + diff --git a/color-picker/src/main/res/values-kk-rKZ/strings.xml b/color-picker/src/main/res/values-kk-rKZ/strings.xml new file mode 100755 index 0000000..1eb2f26 --- /dev/null +++ b/color-picker/src/main/res/values-kk-rKZ/strings.xml @@ -0,0 +1,22 @@ + + + + + "Түсті таңдаңыз" + "%1$d түсі" + "%1$d түсі таңдалды" + diff --git a/color-picker/src/main/res/values-km-rKH/strings.xml b/color-picker/src/main/res/values-km-rKH/strings.xml new file mode 100755 index 0000000..7ca023b --- /dev/null +++ b/color-picker/src/main/res/values-km-rKH/strings.xml @@ -0,0 +1,22 @@ + + + + + "ជ្រើស​ពណ៌" + "ពណ៌ %1$d" + "បាន​ជ្រើស​ពណ៌ %1$d" + diff --git a/color-picker/src/main/res/values-kn-rIN/strings.xml b/color-picker/src/main/res/values-kn-rIN/strings.xml new file mode 100755 index 0000000..35becd1 --- /dev/null +++ b/color-picker/src/main/res/values-kn-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "ಬಣ್ಣ ಆಯ್ಕೆಮಾಡಿ" + "ಬಣ್ಣ %1$d" + "%1$d ಬಣ್ಣವನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ" + diff --git a/color-picker/src/main/res/values-ko/strings.xml b/color-picker/src/main/res/values-ko/strings.xml new file mode 100755 index 0000000..5e06f99 --- /dev/null +++ b/color-picker/src/main/res/values-ko/strings.xml @@ -0,0 +1,22 @@ + + + + + "색상 선택" + "색상 %1$d" + "색상 %1$d이(가) 선택됨" + diff --git a/color-picker/src/main/res/values-ky-rKG/strings.xml b/color-picker/src/main/res/values-ky-rKG/strings.xml new file mode 100755 index 0000000..cb0f889 --- /dev/null +++ b/color-picker/src/main/res/values-ky-rKG/strings.xml @@ -0,0 +1,22 @@ + + + + + "Түс тандаңыз" + "Түс %1$d" + "%1$d түс тандалды" + diff --git a/color-picker/src/main/res/values-lo-rLA/strings.xml b/color-picker/src/main/res/values-lo-rLA/strings.xml new file mode 100755 index 0000000..8f005fe --- /dev/null +++ b/color-picker/src/main/res/values-lo-rLA/strings.xml @@ -0,0 +1,22 @@ + + + + + "ເລືອກ​ສີ​" + "ສີ %1$d" + "​ເລືອກສີ %1$d" + diff --git a/color-picker/src/main/res/values-lt/strings.xml b/color-picker/src/main/res/values-lt/strings.xml new file mode 100755 index 0000000..1610952 --- /dev/null +++ b/color-picker/src/main/res/values-lt/strings.xml @@ -0,0 +1,22 @@ + + + + + "Pasirinkite spalvą" + "%1$d spalva" + "%1$d spalva pasirinkta" + diff --git a/color-picker/src/main/res/values-lv/strings.xml b/color-picker/src/main/res/values-lv/strings.xml new file mode 100755 index 0000000..e844bce --- /dev/null +++ b/color-picker/src/main/res/values-lv/strings.xml @@ -0,0 +1,22 @@ + + + + + "Krāsas atlase" + "Krāsa Nr. %1$d" + "Atlasīta krāsa Nr. %1$d" + diff --git a/color-picker/src/main/res/values-mk-rMK/strings.xml b/color-picker/src/main/res/values-mk-rMK/strings.xml new file mode 100755 index 0000000..46bf1c0 --- /dev/null +++ b/color-picker/src/main/res/values-mk-rMK/strings.xml @@ -0,0 +1,22 @@ + + + + + "Изберете боја" + "Боја %1$d" + "Избрана е %1$d боја" + diff --git a/color-picker/src/main/res/values-ml-rIN/strings.xml b/color-picker/src/main/res/values-ml-rIN/strings.xml new file mode 100755 index 0000000..d5c1230 --- /dev/null +++ b/color-picker/src/main/res/values-ml-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "ഒരു വർണ്ണം തിരഞ്ഞെടുക്കുക" + "വർണ്ണം %1$d" + "വർണ്ണം %1$d തിരഞ്ഞെടുത്തു" + diff --git a/color-picker/src/main/res/values-mn-rMN/strings.xml b/color-picker/src/main/res/values-mn-rMN/strings.xml new file mode 100755 index 0000000..25041c5 --- /dev/null +++ b/color-picker/src/main/res/values-mn-rMN/strings.xml @@ -0,0 +1,22 @@ + + + + + "Өнгө сонгоно уу" + "Өнгө %1$d" + "Өнгө %1$d сонгогдсон" + diff --git a/color-picker/src/main/res/values-mr-rIN/strings.xml b/color-picker/src/main/res/values-mr-rIN/strings.xml new file mode 100755 index 0000000..8b9c617 --- /dev/null +++ b/color-picker/src/main/res/values-mr-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "एक रंग निवडा" + "रंग %1$d" + "%1$d रंग निवडला" + diff --git a/color-picker/src/main/res/values-ms-rMY/strings.xml b/color-picker/src/main/res/values-ms-rMY/strings.xml new file mode 100755 index 0000000..fbac381 --- /dev/null +++ b/color-picker/src/main/res/values-ms-rMY/strings.xml @@ -0,0 +1,22 @@ + + + + + "Pilih Warna" + "Warna %1$d" + "Warna %1$d dipilih" + diff --git a/color-picker/src/main/res/values-my-rMM/strings.xml b/color-picker/src/main/res/values-my-rMM/strings.xml new file mode 100755 index 0000000..eb3f1cb --- /dev/null +++ b/color-picker/src/main/res/values-my-rMM/strings.xml @@ -0,0 +1,22 @@ + + + + + "အရောင်ကို ရွေးပါ" + "အရောင် %1$d" + "အရောင်ကို %1$d ရွေးပြီး" + diff --git a/color-picker/src/main/res/values-nb/strings.xml b/color-picker/src/main/res/values-nb/strings.xml new file mode 100755 index 0000000..ce5a81e --- /dev/null +++ b/color-picker/src/main/res/values-nb/strings.xml @@ -0,0 +1,22 @@ + + + + + "Velg en farge" + "Farge %1$d" + "Farge %1$d er valgt" + diff --git a/color-picker/src/main/res/values-ne-rNP/strings.xml b/color-picker/src/main/res/values-ne-rNP/strings.xml new file mode 100755 index 0000000..d3a7275 --- /dev/null +++ b/color-picker/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,22 @@ + + + + + "एउटा रङ्ग चयन गर्नहोस्" + "रङ्ग %1$d" + "%1$d रङ्ग चयन गरियो" + diff --git a/color-picker/src/main/res/values-nl/strings.xml b/color-picker/src/main/res/values-nl/strings.xml new file mode 100755 index 0000000..2dec3ea --- /dev/null +++ b/color-picker/src/main/res/values-nl/strings.xml @@ -0,0 +1,22 @@ + + + + + "Een kleur selecteren" + "Kleur %1$d" + "Kleur %1$d geselecteerd" + diff --git a/color-picker/src/main/res/values-pa-rIN/strings.xml b/color-picker/src/main/res/values-pa-rIN/strings.xml new file mode 100755 index 0000000..9fecb18 --- /dev/null +++ b/color-picker/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "ਇੱਕ ਰੰਗ ਚੁਣੋ" + "ਰੰਗ %1$d" + "ਰੰਗ %1$d ਚੁਣਿਆ ਗਿਆ" + diff --git a/color-picker/src/main/res/values-pl/strings.xml b/color-picker/src/main/res/values-pl/strings.xml new file mode 100755 index 0000000..04b43ec --- /dev/null +++ b/color-picker/src/main/res/values-pl/strings.xml @@ -0,0 +1,22 @@ + + + + + "Wybierz kolor" + "Kolor %1$d" + "Wybrałeś kolor %1$d" + diff --git a/color-picker/src/main/res/values-pt-rPT/strings.xml b/color-picker/src/main/res/values-pt-rPT/strings.xml new file mode 100755 index 0000000..30fd75b --- /dev/null +++ b/color-picker/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,22 @@ + + + + + "Selecionar uma Cor" + "Cor %1$d" + "Cor %1$d selecionada" + diff --git a/color-picker/src/main/res/values-pt/strings.xml b/color-picker/src/main/res/values-pt/strings.xml new file mode 100755 index 0000000..5eca2e0 --- /dev/null +++ b/color-picker/src/main/res/values-pt/strings.xml @@ -0,0 +1,22 @@ + + + + + "Selecione uma cor" + "Cor %1$d" + "Cor %1$d selecionada" + diff --git a/color-picker/src/main/res/values-ro/strings.xml b/color-picker/src/main/res/values-ro/strings.xml new file mode 100755 index 0000000..bcc245d --- /dev/null +++ b/color-picker/src/main/res/values-ro/strings.xml @@ -0,0 +1,22 @@ + + + + + "Selectați o culoare" + "Culoarea %1$d" + "Culoarea %1$d este selectată" + diff --git a/color-picker/src/main/res/values-ru/strings.xml b/color-picker/src/main/res/values-ru/strings.xml new file mode 100755 index 0000000..0fc7a22 --- /dev/null +++ b/color-picker/src/main/res/values-ru/strings.xml @@ -0,0 +1,22 @@ + + + + + "Выберите цвет" + "Цвет: %1$d" + "Выбран цвет %1$d" + diff --git a/color-picker/src/main/res/values-si-rLK/strings.xml b/color-picker/src/main/res/values-si-rLK/strings.xml new file mode 100755 index 0000000..3aa4bf0 --- /dev/null +++ b/color-picker/src/main/res/values-si-rLK/strings.xml @@ -0,0 +1,22 @@ + + + + + "වර්ණයක් තෝරන්න" + "වර්ණය %1$d" + "%1$d වර්ණය තෝරාගන්නා ලදී" + diff --git a/color-picker/src/main/res/values-sk/strings.xml b/color-picker/src/main/res/values-sk/strings.xml new file mode 100755 index 0000000..900d3b2 --- /dev/null +++ b/color-picker/src/main/res/values-sk/strings.xml @@ -0,0 +1,22 @@ + + + + + "Vyberte farbu" + "Farba %1$d" + "Bola vybratá farba %1$d" + diff --git a/color-picker/src/main/res/values-sl/strings.xml b/color-picker/src/main/res/values-sl/strings.xml new file mode 100755 index 0000000..e62c9d3 --- /dev/null +++ b/color-picker/src/main/res/values-sl/strings.xml @@ -0,0 +1,22 @@ + + + + + "Izberite barvo:" + "Barva %1$d" + "Izbrana je barva %1$d" + diff --git a/color-picker/src/main/res/values-sq-rAL/strings.xml b/color-picker/src/main/res/values-sq-rAL/strings.xml new file mode 100755 index 0000000..2e21621 --- /dev/null +++ b/color-picker/src/main/res/values-sq-rAL/strings.xml @@ -0,0 +1,22 @@ + + + + + "Zgjidh një ngjyrë" + "Ngjyra %1$d" + "U zgjodh ngjyra %1$d" + diff --git a/color-picker/src/main/res/values-sr/strings.xml b/color-picker/src/main/res/values-sr/strings.xml new file mode 100755 index 0000000..6ca8340 --- /dev/null +++ b/color-picker/src/main/res/values-sr/strings.xml @@ -0,0 +1,22 @@ + + + + + "Изаберите боју" + "Боја %1$d" + "Боја %1$d је изабрана" + diff --git a/color-picker/src/main/res/values-sv/strings.xml b/color-picker/src/main/res/values-sv/strings.xml new file mode 100755 index 0000000..ac2514b --- /dev/null +++ b/color-picker/src/main/res/values-sv/strings.xml @@ -0,0 +1,22 @@ + + + + + "Välj en färg" + "Färg %1$d" + "Färg %1$d vald" + diff --git a/color-picker/src/main/res/values-sw/strings.xml b/color-picker/src/main/res/values-sw/strings.xml new file mode 100755 index 0000000..fb1d587 --- /dev/null +++ b/color-picker/src/main/res/values-sw/strings.xml @@ -0,0 +1,22 @@ + + + + + "Chagua Rangi" + "Rangi %1$d" + "Rangi %1$d iliyochaguliwa" + diff --git a/color-picker/src/main/res/values-ta-rIN/strings.xml b/color-picker/src/main/res/values-ta-rIN/strings.xml new file mode 100755 index 0000000..bb5a952 --- /dev/null +++ b/color-picker/src/main/res/values-ta-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "வண்ணத்தைத் தேர்ந்தெடுக்கவும்" + "வண்ணம் %1$d" + "வண்ணம் %1$d தேர்ந்தெடுக்கப்பட்டது" + diff --git a/color-picker/src/main/res/values-te-rIN/strings.xml b/color-picker/src/main/res/values-te-rIN/strings.xml new file mode 100755 index 0000000..97332f9 --- /dev/null +++ b/color-picker/src/main/res/values-te-rIN/strings.xml @@ -0,0 +1,22 @@ + + + + + "రంగును ఎంచుకోండి" + "రంగు %1$d" + "రంగు %1$d ఎంచుకోబడింది" + diff --git a/color-picker/src/main/res/values-th/strings.xml b/color-picker/src/main/res/values-th/strings.xml new file mode 100755 index 0000000..e2d2934 --- /dev/null +++ b/color-picker/src/main/res/values-th/strings.xml @@ -0,0 +1,22 @@ + + + + + "เลือกสี" + "สี %1$d" + "เลือกสี %1$d" + diff --git a/color-picker/src/main/res/values-tl/strings.xml b/color-picker/src/main/res/values-tl/strings.xml new file mode 100755 index 0000000..e7fb303 --- /dev/null +++ b/color-picker/src/main/res/values-tl/strings.xml @@ -0,0 +1,22 @@ + + + + + "Pumili ng Kulay" + "Kulay na %1$d" + "Napili ang kulay na %1$d" + diff --git a/color-picker/src/main/res/values-tr/strings.xml b/color-picker/src/main/res/values-tr/strings.xml new file mode 100755 index 0000000..4157d96 --- /dev/null +++ b/color-picker/src/main/res/values-tr/strings.xml @@ -0,0 +1,22 @@ + + + + + "Bir Renk Seçin" + "Renk %1$d" + "Renk %1$d seçildi" + diff --git a/color-picker/src/main/res/values-uk/strings.xml b/color-picker/src/main/res/values-uk/strings.xml new file mode 100755 index 0000000..a8bf87b --- /dev/null +++ b/color-picker/src/main/res/values-uk/strings.xml @@ -0,0 +1,22 @@ + + + + + "Вибрати колір" + "Колір %1$d" + "Вибрано колір: %1$d" + diff --git a/color-picker/src/main/res/values-ur-rPK/strings.xml b/color-picker/src/main/res/values-ur-rPK/strings.xml new file mode 100755 index 0000000..f748471 --- /dev/null +++ b/color-picker/src/main/res/values-ur-rPK/strings.xml @@ -0,0 +1,22 @@ + + + + + "ایک رنگ منتخب کریں" + "رنگ %1$d" + "رنگ %1$d منتخب کیا گیا" + diff --git a/color-picker/src/main/res/values-uz-rUZ/strings.xml b/color-picker/src/main/res/values-uz-rUZ/strings.xml new file mode 100755 index 0000000..81ac010 --- /dev/null +++ b/color-picker/src/main/res/values-uz-rUZ/strings.xml @@ -0,0 +1,22 @@ + + + + + "Rang tanlash" + "%1$d rangi" + "%1$d rangi tanlandi" + diff --git a/color-picker/src/main/res/values-vi/strings.xml b/color-picker/src/main/res/values-vi/strings.xml new file mode 100755 index 0000000..e027169 --- /dev/null +++ b/color-picker/src/main/res/values-vi/strings.xml @@ -0,0 +1,22 @@ + + + + + "Chọn màu" + "Màu %1$d" + "Đã chọn màu %1$d" + diff --git a/color-picker/src/main/res/values-zh-rCN/strings.xml b/color-picker/src/main/res/values-zh-rCN/strings.xml new file mode 100755 index 0000000..3e12d6f --- /dev/null +++ b/color-picker/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,35 @@ + + + + + "选择颜色" + "颜色 %1$d" + "已选择颜色 %1$d" + + 番茄红 + 橘红 + 香蕉黄 + 罗勒绿 + 鼠尾草绿 + 孔雀蓝 + 蓝莓色 + 薰衣草色 + 葡萄紫 + 红鹤色 + 石墨黑 + 默认颜色 + diff --git a/color-picker/src/main/res/values-zh-rHK/strings.xml b/color-picker/src/main/res/values-zh-rHK/strings.xml new file mode 100755 index 0000000..5fffb7f --- /dev/null +++ b/color-picker/src/main/res/values-zh-rHK/strings.xml @@ -0,0 +1,22 @@ + + + + + "選取顏色" + "顏色 %1$d" + "已選取顏色 %1$d" + diff --git a/color-picker/src/main/res/values-zh-rTW/strings.xml b/color-picker/src/main/res/values-zh-rTW/strings.xml new file mode 100755 index 0000000..86e4782 --- /dev/null +++ b/color-picker/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,22 @@ + + + + + "選取顏色" + "色彩 %1$d" + "已選取色彩 %1$d" + diff --git a/color-picker/src/main/res/values-zu/strings.xml b/color-picker/src/main/res/values-zu/strings.xml new file mode 100755 index 0000000..98e213f --- /dev/null +++ b/color-picker/src/main/res/values-zu/strings.xml @@ -0,0 +1,22 @@ + + + + + "Khetha umbala" + "Umbala we-%1$d" + "Umbala we-%1$d ukhethiwe" + diff --git a/color-picker/src/main/res/values/arrays.xml b/color-picker/src/main/res/values/arrays.xml new file mode 100755 index 0000000..c975211 --- /dev/null +++ b/color-picker/src/main/res/values/arrays.xml @@ -0,0 +1,29 @@ + + + + @color/tomato + @color/tangerine + @color/banana + @color/basil + @color/sage + @color/peacock + @color/blueberry + @color/lavender + @color/grape + @color/flamingo + @color/graphite + + + @string/tomato + @string/tangerine + @string/banana + @string/basil + @string/sage + @string/peacock + @string/blueberry + @string/lavender + @string/grape + @string/flamingo + @string/graphite + + \ No newline at end of file diff --git a/color-picker/src/main/res/values/attrs.xml b/color-picker/src/main/res/values/attrs.xml new file mode 100755 index 0000000..5176273 --- /dev/null +++ b/color-picker/src/main/res/values/attrs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/color-picker/src/main/res/values/colors.xml b/color-picker/src/main/res/values/colors.xml new file mode 100755 index 0000000..a80da26 --- /dev/null +++ b/color-picker/src/main/res/values/colors.xml @@ -0,0 +1,30 @@ + + + + #33b5e5 + #ff8800 + #ff4444 + + #FF007F + #FF0000 + #FF7F00 + #FFFF00 + #00FF00 + #00FFFF + #007FFF + #0000FF + #7F00FF + #FF00FF + + #d50000 + #f4511e + #f6bf26 + #0b8043 + #33b679 + #039be5 + #3f51b5 + #7986cb + #8e24aa + #e67c73 + #616161 + \ No newline at end of file diff --git a/color-picker/src/main/res/values/dimens.xml b/color-picker/src/main/res/values/dimens.xml new file mode 100755 index 0000000..fc78f2f --- /dev/null +++ b/color-picker/src/main/res/values/dimens.xml @@ -0,0 +1,21 @@ + + + + 64dip + 48dip + 8dip + 4dip + \ No newline at end of file diff --git a/color-picker/src/main/res/values/strings.xml b/color-picker/src/main/res/values/strings.xml new file mode 100755 index 0000000..00af993 --- /dev/null +++ b/color-picker/src/main/res/values/strings.xml @@ -0,0 +1,36 @@ + + + + + Select a Color + + Color %1$d + + Color %1$d selected + + Tomato + Tangerine + Banana + Basil + Sage + Peacock + Blueberry + Lavender + Grape + Flamingo + Graphite + Default color + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..aac7c9b --- /dev/null +++ b/gradle.properties @@ -0,0 +1,17 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..04e285f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Dec 28 10:00:20 PST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..12fb689 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app', ':color-picker'