Repository: nicholasrout/lens-launcher Branch: master Commit: dd9bd11905ff Files: 87 Total size: 226.9 KB Directory structure: gitextract_6p3thwce/ ├── .gitignore ├── LICENSE.md ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── build.gradle.orig │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── nickrout/ │ │ └── lenslauncher/ │ │ └── ApplicationTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── AndroidManifest.xml.orig │ │ ├── java/ │ │ │ └── nickrout/ │ │ │ └── lenslauncher/ │ │ │ ├── AppsSingleton.java │ │ │ ├── LensLauncherApplication.java │ │ │ ├── adapter/ │ │ │ │ └── AppRecyclerAdapter.java │ │ │ ├── background/ │ │ │ │ ├── BackgroundChangedObservable.java │ │ │ │ ├── BroadcastReceivers.java │ │ │ │ ├── EditedObservable.java │ │ │ │ ├── LoadedObservable.java │ │ │ │ ├── NightModeObservable.java │ │ │ │ ├── SortAppsTask.java │ │ │ │ ├── UpdateAppsTask.java │ │ │ │ ├── UpdatedObservable.java │ │ │ │ └── VisibilityChangedObservable.java │ │ │ ├── model/ │ │ │ │ ├── App.java │ │ │ │ ├── AppPersistent.java │ │ │ │ └── Grid.java │ │ │ ├── ui/ │ │ │ │ ├── AboutActivity.java │ │ │ │ ├── AppsFragment.java │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── FakeLauncherActivity.java │ │ │ │ ├── HomeActivity.java │ │ │ │ ├── LensFragment.java │ │ │ │ ├── LensView.java │ │ │ │ ├── SettingsActivity.java │ │ │ │ └── SettingsFragment.java │ │ │ └── util/ │ │ │ ├── AppSorter.java │ │ │ ├── AppUtil.java │ │ │ ├── BitmapUtil.java │ │ │ ├── ColorUtil.java │ │ │ ├── IconPackManager.java │ │ │ ├── LauncherUtil.java │ │ │ ├── LensCalculator.java │ │ │ ├── NightModeUtil.java │ │ │ └── Settings.java │ │ └── res/ │ │ ├── anim/ │ │ │ ├── fade_in.xml │ │ │ ├── fade_out.xml │ │ │ ├── slide_in_left.xml │ │ │ ├── slide_in_right.xml │ │ │ ├── slide_out_left.xml │ │ │ └── slide_out_right.xml │ │ ├── animator/ │ │ │ └── raise.xml │ │ ├── drawable/ │ │ │ ├── ic_launcher_background.xml │ │ │ └── ic_launcher_foreground.xml │ │ ├── layout/ │ │ │ ├── activity_about.xml │ │ │ ├── activity_fake_launcher.xml │ │ │ ├── activity_home.xml │ │ │ ├── activity_settings.xml │ │ │ ├── fragment_apps.xml │ │ │ ├── fragment_lens.xml │ │ │ ├── fragment_settings.xml │ │ │ ├── layout_pro.xml │ │ │ └── recycler_item_app.xml │ │ ├── menu/ │ │ │ ├── menu_app.xml │ │ │ └── menu_settings.xml │ │ ├── mipmap-anydpi-v26/ │ │ │ └── ic_launcher.xml │ │ ├── values/ │ │ │ ├── arrays.xml │ │ │ ├── colors.xml │ │ │ ├── dimen.xml │ │ │ ├── integers.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-land/ │ │ │ └── dimen.xml │ │ ├── values-night/ │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ ├── values-w640dp/ │ │ │ └── integers.xml │ │ ├── values-w820dp/ │ │ │ └── dimen.xml │ │ └── xml/ │ │ └── shortcuts.xml │ └── test/ │ └── java/ │ └── nickrout/ │ └── lenslauncher/ │ └── ExampleUnitTest.java ├── build.gradle ├── docker-compose.yml ├── docker-entrypoint.sh ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── resources/ │ └── art/ │ └── lens_launcher.sketch └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # built application files *.apk *.ap_ # files for the dex VM *.dex # Java class files *.class # generated files bin/ gen/ out/ # Local configuration file (sdk path, etc) local.properties # Eclipse project files .classpath .project # Android Studio .idea/ .gradle build/ /*/local.properties local.properties /*/out /*/*/build /*/*/production *.iml *.iws *.ipr *~ *.swp proguard/ *.log .navigation/ captures/ # Keystore files *.jks # Android SDK /android-sdk.zip /android-studio ================================================ FILE: LICENSE.md ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2017 Nicholas Rout 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. ================================================ FILE: README.md ================================================ # Lens Launcher Lens Launcher is a unique, efficient way to browse and launch your apps. ![Alt text](resources/art/lens_launcher_demo.gif?raw=true "Title") Instead of scrolling long lists or swiping through multiple pages, Lens Launcher implements two unique features: • An Equispaced Grid that displays all of your apps, regardless of screen size or app count. • A Graphical Fisheye Lens to quickly zoom, pan and launch apps using touch gestures. Lens Launcher also includes a full Settings menu to tweak all aspects of the Lens; Distortion, Scaling, Icon Size, Haptic Feedback, etc. The Graphical Fisheye Lens algorithm is derived from methods proposed by Manojit Sarkar and Marc H. Brown. Their original 1993 paper, titled Graphical Fisheye Views, can be found here: https://drive.google.com/open?id=0B6Nqy9y098RLa2pqNVFUcUhIZXM Major contributions to Lens Launcher have been made by Rish Bhardwaj (@CreaRo) You can download Lens Launcher on Google Play: https://play.google.com/store/apps/details?id=nickrout.lenslauncher ## Build from Source If you'd like to build from source, you may do so easily with [Docker][1], but if you are wanting to contribute it may be better to install the build tools on your machine. ### Easy Way Building the APK is extremely easy using [`docker-compose`][2], all that is needed is to run the following inside the repo's directory: ```bash docker-compose run --rm gradle ``` Then download the APK inside `lens-launcher/app/build/outputs/apk/app-release.apk` to your phone to install. ### For Development 1. Install the latest Android SDK from [Android Studio][3] and accept the licenses. 2. Install Java JDK 8 from [Oracle][4]. 3. Run `keytool` and create a keystore following the command prompts. 4. Generate an APK with `gradlew` like below: ```bash gradlew assembleRelease \ -Pandroid.injected.signing.key.alias=$KEY_ALIAS \ -Pandroid.injected.signing.store.file=$KEY_STORE_PATH \ -Pandroid.injected.signing.store.password=$KEY_STORE_PASSWORD \ -Pandroid.injected.signing.key.password=$KEY_PASSWORD; ``` [1]: https://docs.docker.com/ [2]: https://docs.docker.com/compose/install/ [3]: https://developer.android.com/studio/index.html [4]: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { namespace "nickrout.lenslauncher" compileSdk 34 defaultConfig { applicationId "nickrout.lenslauncher" minSdkVersion 15 targetSdkVersion 34 versionCode 46 versionName "3.0.1" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation 'junit:junit:4.13.2' implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support:design:26.1.0' implementation 'com.android.support:recyclerview-v7:26.1.0' implementation 'com.android.support:cardview-v7:26.1.0' implementation 'com.android.support:palette-v7:26.1.0' implementation 'com.github.satyan:sugar:1.5' implementation 'com.afollestad.material-dialogs:core:0.9.5.0' implementation 'com.afollestad.material-dialogs:commons:0.9.5.0' implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' } tasks.withType(JavaCompile).configureEach{ options.fork = true options.forkOptions.jvmArgs +=[ '--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED', '--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED', '--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED'] } ================================================ FILE: app/build.gradle.orig ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.3" defaultConfig { applicationId "nickrout.lenslauncher" minSdkVersion 16 targetSdkVersion 23 versionCode 16 versionName "1.3.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.4.0' <<<<<<< HEAD compile 'com.android.support:design:23.1.1' compile 'com.android.support:recyclerview-v7:23.3.0' compile 'com.android.support:cardview-v7:23.3.0' ======= compile 'com.android.support:palette-v7:23.4.0' >>>>>>> 85209e8a5dc31e126a1f49a543f70d299720c052 compile 'com.github.satyan:sugar:1.5' compile 'com.afollestad.material-dialogs:core:0.8.5.8' compile 'com.afollestad.material-dialogs:commons:0.8.5.8' compile 'com.makeramen:dragsortadapter:1.3.0' compile 'com.jakewharton:butterknife:7.0.1' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Users/nicholasrout/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 *; #} ================================================ FILE: app/src/androidTest/java/nickrout/lenslauncher/ApplicationTest.java ================================================ package nickrout.lenslauncher; import android.app.Application; import android.test.ApplicationTestCase; /** * Testing Fundamentals */ public class ApplicationTest extends ApplicationTestCase { public ApplicationTest() { super(Application.class); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/AndroidManifest.xml.orig ================================================ ======= android:value="3" /> >>>>>>> 0c9969d213bbd5efac04492932bd13b6d97bacc0 ================================================ FILE: app/src/main/java/nickrout/lenslauncher/AppsSingleton.java ================================================ package nickrout.lenslauncher; import android.graphics.Bitmap; import java.util.ArrayList; import nickrout.lenslauncher.model.App; /** * Created by nicholasrout on 2016/05/30. */ public class AppsSingleton { private static AppsSingleton mAppsSingleton; private ArrayList mApps; private ArrayList mAppIcons; private AppsSingleton() {} public static AppsSingleton getInstance() { if (mAppsSingleton == null) { mAppsSingleton = new AppsSingleton(); } return mAppsSingleton; } public ArrayList getApps() { ArrayList apps = new ArrayList<>(); if (mApps == null) { return apps; } apps.addAll(mApps); return apps; } public void setApps(ArrayList apps) { mApps = apps; } public ArrayList getAppIcons() { ArrayList appIcons = new ArrayList<>(); if (mAppIcons == null) { return appIcons; } appIcons.addAll(mAppIcons); return appIcons; } public void setAppIcons(ArrayList appIcons) { mAppIcons = appIcons; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/LensLauncherApplication.java ================================================ package nickrout.lenslauncher; import com.orm.SugarApp; import java.util.Observable; import java.util.Observer; import nickrout.lenslauncher.background.SortAppsTask; import nickrout.lenslauncher.background.EditedObservable; import nickrout.lenslauncher.background.UpdatedObservable; import nickrout.lenslauncher.background.UpdateAppsTask; /** * Created by nicholasrout on 2016/06/12. */ public class LensLauncherApplication extends SugarApp implements Observer { @Override public void onCreate() { super.onCreate(); UpdatedObservable.getInstance().addObserver(this); EditedObservable.getInstance().addObserver(this); updateApps(); } @Override public void update(Observable observable, Object data) { if (observable instanceof UpdatedObservable) { updateApps(); } else if (observable instanceof EditedObservable) { editApps(); } } private void updateApps() { new UpdateAppsTask( getPackageManager(), getApplicationContext(), this) .execute(); } private void editApps() { new SortAppsTask( getApplicationContext(), this) .execute(); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/adapter/AppRecyclerAdapter.java ================================================ package nickrout.lenslauncher.adapter; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.net.Uri; import android.provider.Settings; import android.support.design.widget.Snackbar; import android.support.v7.widget.CardView; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import nickrout.lenslauncher.R; import nickrout.lenslauncher.background.BroadcastReceivers; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.model.AppPersistent; import nickrout.lenslauncher.ui.SettingsActivity; import nickrout.lenslauncher.util.AppUtil; public class AppRecyclerAdapter extends RecyclerView.Adapter { public static final String TAG = "AppRecyclerAdapter"; private Context mContext; private final List mApps; public AppRecyclerAdapter(Context mContext, List mApps) { this.mContext = mContext; this.mApps = mApps; } public App getItemForPosition(int position) { return mApps.get(position); } @Override public int getItemCount() { return mApps.size(); } @Override public long getItemId(int position) { return mApps.get(position).getId(); } @Override public AppViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View view = inflater.inflate(R.layout.recycler_item_app, parent, false); final AppViewHolder holder = new AppViewHolder(view, mContext); holder.setOnClickListeners(); return holder; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { App app = getItemForPosition(position); if (app == null) { return; } AppViewHolder appViewHolder = (AppViewHolder) holder; appViewHolder.setAppElement(app); } public static class AppViewHolder extends RecyclerView.ViewHolder implements PopupMenu.OnMenuItemClickListener { @BindView(R.id.element_app_container) CardView mContainer; @BindView(R.id.element_app_label) TextView mLabel; @BindView(R.id.element_app_icon) ImageView mIcon; @BindView(R.id.element_app_hide) ImageView mToggleAppVisibility; @BindView(R.id.element_app_menu) ImageView mMenu; private App mApp; private Context mContext; public AppViewHolder(View itemView, Context context) { super(itemView); ButterKnife.bind(this, itemView); this.mContext = context; } public void setAppElement(App app) { this.mApp = app; mLabel.setText(mApp.getLabel()); mIcon.setImageBitmap(mApp.getIcon()); boolean isAppVisible = AppPersistent.getAppVisibility(mApp.getPackageName().toString(), mApp.getName().toString()); if (isAppVisible) { mToggleAppVisibility.setImageResource(R.drawable.ic_visibility_grey_24dp); } else { mToggleAppVisibility.setImageResource(R.drawable.ic_visibility_off_grey_24dp); } if (mApp.getPackageName().toString().equals("nickrout.lenslauncher")) { mToggleAppVisibility.setVisibility(View.INVISIBLE); } else { mToggleAppVisibility.setVisibility(View.VISIBLE); } } public void toggleAppVisibility(App app) { this.mApp = app; boolean isAppVisible = AppPersistent.getAppVisibility(mApp.getPackageName().toString(), mApp.getName().toString()); AppPersistent.setAppVisibility( mApp.getPackageName().toString(), mApp.getName().toString(), !isAppVisible); if (isAppVisible) { Snackbar.make(mContainer, mApp.getLabel() + " is now hidden", Snackbar.LENGTH_LONG).show(); mToggleAppVisibility.setImageResource(R.drawable.ic_visibility_off_grey_24dp); } else { Snackbar.make(mContainer, mApp.getLabel() + " is now visible", Snackbar.LENGTH_LONG).show(); mToggleAppVisibility.setImageResource(R.drawable.ic_visibility_grey_24dp); } } private void sendChangeAppsVisibilityBroadcast() { if (mContext == null) { return; } if (!(mContext instanceof SettingsActivity)) { return; } SettingsActivity settingsActivity = (SettingsActivity) mContext; Intent changeAppsVisibilityIntent = new Intent(settingsActivity, BroadcastReceivers.AppsVisibilityChangedReceiver.class); settingsActivity.sendBroadcast(changeAppsVisibilityIntent); } public void setOnClickListeners() { itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { AppUtil.launchComponent( mContext, mApp.getPackageName().toString(), mApp.getName().toString(), itemView, new Rect(0, 0, itemView.getMeasuredWidth(), itemView.getMeasuredHeight())); } }); mToggleAppVisibility.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mApp != null) { sendChangeAppsVisibilityBroadcast(); toggleAppVisibility(mApp); } else { Snackbar.make(mContainer, mContext.getString(R.string.error_app_not_found), Snackbar.LENGTH_LONG).show(); } } }); mMenu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { PopupMenu popupMenu = new PopupMenu(mContext, view); popupMenu.setOnMenuItemClickListener(AppViewHolder.this); popupMenu.inflate(R.menu.menu_app); popupMenu.show(); } }); } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.menu_item_element_app_info: try { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.setData(Uri.parse("package:" + mApp.getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } catch (ActivityNotFoundException e) { Toast.makeText(mContext, R.string.error_app_not_found, Toast.LENGTH_SHORT).show(); } return true; case R.id.menu_item_element_uninstall: try { Intent intent = new Intent(Intent.ACTION_DELETE); intent.setData(Uri.parse("package:" + mApp.getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } catch (ActivityNotFoundException e) { Toast.makeText(mContext, R.string.error_app_not_found, Toast.LENGTH_SHORT).show(); } return true; } return false; } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/BackgroundChangedObservable.java ================================================ package nickrout.lenslauncher.background; import java.util.Observable; public class BackgroundChangedObservable extends Observable { private static BackgroundChangedObservable instance = new BackgroundChangedObservable(); public static BackgroundChangedObservable getInstance() { return instance; } private BackgroundChangedObservable() { } public void updateValue(Object data) { synchronized (this) { setChanged(); notifyObservers(data); } } public void update() { updateValue(null); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/BroadcastReceivers.java ================================================ package nickrout.lenslauncher.background; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; /** * Created by nicholasrout on 2016/06/12. */ public class BroadcastReceivers { public static class AppsUpdatedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { UpdatedObservable.getInstance().update(); } } public static class AppsEditedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { EditedObservable.getInstance().update(); } } public static class AppsVisibilityChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { VisibilityChangedObservable.getInstance().update(); } } public static class AppsLoadedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { LoadedObservable.getInstance().update(); } } public static class BackgroundChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { BackgroundChangedObservable.getInstance().update(); } } public static class NightModeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { NightModeObservable.getInstance().update(); } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/EditedObservable.java ================================================ package nickrout.lenslauncher.background; import java.util.Observable; /** * Created by nicholasrout on 2016/06/23. */ public class EditedObservable extends Observable { private static EditedObservable instance = new EditedObservable(); public static EditedObservable getInstance() { return instance; } private EditedObservable() { } public void updateValue(Object data) { synchronized (this) { setChanged(); notifyObservers(data); } } public void update() { updateValue(null); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/LoadedObservable.java ================================================ package nickrout.lenslauncher.background; import java.util.Observable; /** * Created by nicholasrout on 2016/06/12. */ public class LoadedObservable extends Observable { private static LoadedObservable instance = new LoadedObservable(); public static LoadedObservable getInstance() { return instance; } private LoadedObservable() { } public void updateValue(Object data) { synchronized (this) { setChanged(); notifyObservers(data); } } public void update() { updateValue(null); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/NightModeObservable.java ================================================ package nickrout.lenslauncher.background; import java.util.Observable; /** * Created by nicholasrout on 2017/01/15. */ public class NightModeObservable extends Observable { private static NightModeObservable instance = new NightModeObservable(); public static NightModeObservable getInstance() { return instance; } private NightModeObservable() { } public void updateValue(Object data) { synchronized (this) { setChanged(); notifyObservers(data); } } public void update() { updateValue(null); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/SortAppsTask.java ================================================ package nickrout.lenslauncher.background; import android.app.Application; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.os.AsyncTask; import java.util.ArrayList; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.util.AppSorter; import nickrout.lenslauncher.AppsSingleton; import nickrout.lenslauncher.util.Settings; /** * Created by nicholasrout on 2016/06/23. */ public class SortAppsTask extends AsyncTask { private Context mContext; private Application mApplication; private Settings mSettings; private ArrayList mApps; private ArrayList mAppIcons; public SortAppsTask(Context context, Application application) { mContext = context; mApplication = application; mSettings = new Settings(mContext); } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { ArrayList apps = AppsSingleton.getInstance().getApps(); AppSorter.sort(apps, mSettings.getSortType()); mApps = new ArrayList<>(); mAppIcons = new ArrayList<>(); for (int i = 0; i < apps.size(); i++) { App app = apps.get(i); Bitmap appIcon = app.getIcon(); if (appIcon != null) { mApps.add(app); mAppIcons.add(appIcon); } } return null; } @Override protected void onPostExecute(Void result) { AppsSingleton.getInstance().setApps(mApps); AppsSingleton.getInstance().setAppIcons(mAppIcons); Intent appsLoadedIntent = new Intent(mApplication, BroadcastReceivers.AppsLoadedReceiver.class); mApplication.sendBroadcast(appsLoadedIntent); super.onPostExecute(result); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/UpdateAppsTask.java ================================================ package nickrout.lenslauncher.background; import android.app.Application; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.os.AsyncTask; import java.util.ArrayList; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.util.AppUtil; import nickrout.lenslauncher.AppsSingleton; import nickrout.lenslauncher.util.Settings; /** * Created by rish on 26/5/16. */ public class UpdateAppsTask extends AsyncTask { private PackageManager mPackageManager; private Context mContext; private Application mApplication; private Settings mSettings; private ArrayList mApps; private ArrayList mAppIcons; public UpdateAppsTask(PackageManager packageManager, Context context, Application application) { this.mPackageManager = packageManager; this.mContext = context; this.mApplication = application; this.mSettings = new Settings(context); } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Void doInBackground(Void... params) { ArrayList apps = AppUtil.getApps( mPackageManager, mContext, mApplication, mSettings.getString(Settings.KEY_ICON_PACK_LABEL_NAME), mSettings.getSortType()); mApps = new ArrayList<>(); mAppIcons = new ArrayList<>(); for (int i = 0; i < apps.size(); i++) { App app = apps.get(i); Bitmap appIcon = app.getIcon(); if (appIcon != null) { mApps.add(app); mAppIcons.add(appIcon); } } return null; } @Override protected void onPostExecute(Void result) { AppsSingleton.getInstance().setApps(mApps); AppsSingleton.getInstance().setAppIcons(mAppIcons); Intent appsLoadedIntent = new Intent(mApplication, BroadcastReceivers.AppsLoadedReceiver.class); mApplication.sendBroadcast(appsLoadedIntent); super.onPostExecute(result); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/UpdatedObservable.java ================================================ package nickrout.lenslauncher.background; import java.util.Observable; /** * Created by nickrout on 2016/04/08. */ public class UpdatedObservable extends Observable { private static UpdatedObservable instance = new UpdatedObservable(); public static UpdatedObservable getInstance() { return instance; } private UpdatedObservable() { } public void updateValue(Object data) { synchronized (this) { setChanged(); notifyObservers(data); } } public void update() { updateValue(null); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/background/VisibilityChangedObservable.java ================================================ package nickrout.lenslauncher.background; import java.util.Observable; public class VisibilityChangedObservable extends Observable { private static VisibilityChangedObservable instance = new VisibilityChangedObservable(); public static VisibilityChangedObservable getInstance() { return instance; } private VisibilityChangedObservable() { } public void updateValue(Object data) { synchronized (this) { setChanged(); notifyObservers(data); } } public void update() { updateValue(null); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/model/App.java ================================================ package nickrout.lenslauncher.model; import android.graphics.Bitmap; import android.support.annotation.ColorInt; /** * Created by nickrout on 2016/04/02. */ public class App { private int mId; private CharSequence mLabel; private CharSequence mPackageName; private CharSequence mName; private int mIconResId; private Bitmap mIcon; private long mInstallDate; private @ColorInt int mPaletteColor; public App() { } public int getId() { return mId; } public void setId(int mId) { this.mId = mId; } public CharSequence getLabel() { return mLabel; } public void setLabel(CharSequence label) { mLabel = label; } public CharSequence getPackageName() { return mPackageName; } public void setPackageName(CharSequence packageName) { mPackageName = packageName; } public CharSequence getName() { return mName; } public void setName(CharSequence name) { mName = name; } public int getIconResId() { return mIconResId; } public void setIconResId(int iconResId) { mIconResId = iconResId; } public Bitmap getIcon() { return mIcon; } public void setIcon(Bitmap icon) { mIcon = icon; } public long getInstallDate() { return mInstallDate; } public void setInstallDate(long mInstallDate) { this.mInstallDate = mInstallDate; } public int getPaletteColor() { return mPaletteColor; } public void setPaletteColor(int paletteColor) { mPaletteColor = paletteColor; } @Override public String toString() { return "App{" + "mId=" + mId + ", mLabel=" + mLabel + ", mPackageName=" + mPackageName + ", mName=" + mName + ", mIconResId=" + mIconResId + ", mIcon=" + mIcon + ", mInstallDate=" + mInstallDate + ", mPaletteColor=" + mPaletteColor + '}'; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/model/AppPersistent.java ================================================ package nickrout.lenslauncher.model; import com.orm.SugarRecord; import com.orm.query.Condition; import com.orm.query.Select; import com.orm.util.NamingHelper; /** * Created by rish on 20/5/16. */ public class AppPersistent extends SugarRecord { private static final String TAG = "AppPersistent"; /* Required Default Constructor */ public AppPersistent() { } private String mPackageName; private String mName; private String mIdentifier; private long mOpenCount; private int mOrderNumber; private boolean mAppVisible; private static boolean DEFAULT_APP_VISIBILITY = true; private static int DEFAULT_ORDER_NUMBER = -1; private static long DEFAULT_OPEN_COUNT = 1; public AppPersistent(String packageName, String name, long openCount, int orderNumber, boolean appVisible) { this.mPackageName = packageName; this.mName = name; this.mIdentifier = AppPersistent.generateIdentifier(packageName, name); this.mOpenCount = openCount; this.mOrderNumber = orderNumber; this.mAppVisible = appVisible; } public String getPackageName() { return mPackageName; } public void setPackageName(String packageName) { this.mPackageName = packageName; this.mIdentifier = AppPersistent.generateIdentifier(this.mPackageName, this.mName); } public String getName() { return mName; } public void setName(String name) { mName = name; this.mIdentifier = AppPersistent.generateIdentifier(this.mPackageName, this.mName); } public String getIdentifier() { return mIdentifier; } public long getOpenCount() { return mOpenCount; } public void setOpenCount(long openCount) { this.mOpenCount = openCount; } public int getOrderNumber() { return mOrderNumber; } public void setOrderNumber(int orderNumber) { this.mOrderNumber = orderNumber; } public boolean isAppVisible() { return mAppVisible; } public void setAppVisible(boolean appVisible) { this.mAppVisible = appVisible; } @Override public String toString() { return "AppPersistent{" + "mPackageName='" + mPackageName + '\'' + ", mName='" + mName + '\'' + ", mIdentifier='" + mIdentifier + '\'' + ", mOpenCount=" + mOpenCount + ", mOrderNumber=" + mOrderNumber + ", mAppVisible=" + mAppVisible + '}'; } public static String generateIdentifier(String packageName, String name) { return packageName + "-" + name; } public static void incrementAppCount(String packageName, String name) { String identifier = AppPersistent.generateIdentifier(packageName, name); AppPersistent appPersistent = Select.from(AppPersistent.class).where(Condition.prop(NamingHelper.toSQLNameDefault("mIdentifier")).eq(identifier)).first(); if (appPersistent != null) { appPersistent.setOpenCount(appPersistent.getOpenCount() + 1); appPersistent.save(); } else { AppPersistent newAppPersistent = new AppPersistent(packageName, name, DEFAULT_OPEN_COUNT, DEFAULT_ORDER_NUMBER, DEFAULT_APP_VISIBILITY); newAppPersistent.save(); } } public static void setAppOrderNumber(String packageName, String name, int orderNumber) { String identifier = AppPersistent.generateIdentifier(packageName, name); AppPersistent appPersistent = Select.from(AppPersistent.class).where(Condition.prop(NamingHelper.toSQLNameDefault("mIdentifier")).eq(identifier)).first(); if (appPersistent != null) { appPersistent.setOrderNumber(orderNumber); appPersistent.save(); } else { AppPersistent newAppPersistent = new AppPersistent(packageName, name, DEFAULT_OPEN_COUNT, DEFAULT_ORDER_NUMBER, DEFAULT_APP_VISIBILITY); newAppPersistent.save(); } } public static boolean getAppVisibility(String packageName, String name) { String identifier = AppPersistent.generateIdentifier(packageName, name); AppPersistent appPersistent = Select.from(AppPersistent.class).where(Condition.prop(NamingHelper.toSQLNameDefault("mIdentifier")).eq(identifier)).first(); if (appPersistent != null) { return appPersistent.isAppVisible(); } else { return true; } } public static void setAppVisibility(String packageName, String name, boolean mHideApp) { String identifier = AppPersistent.generateIdentifier(packageName, name); AppPersistent appPersistent = Select.from(AppPersistent.class).where(Condition.prop(NamingHelper.toSQLNameDefault("mIdentifier")).eq(identifier)).first(); if (appPersistent != null) { appPersistent.setAppVisible(mHideApp); appPersistent.save(); } else { AppPersistent newAppPersistent = new AppPersistent(packageName, name, DEFAULT_OPEN_COUNT, DEFAULT_ORDER_NUMBER, mHideApp); newAppPersistent.save(); } } public static long getAppOpenCount(String packageName, String name) { String identifier = AppPersistent.generateIdentifier(packageName, name); AppPersistent appPersistent = Select.from(AppPersistent.class).where(Condition.prop(NamingHelper.toSQLNameDefault("mIdentifier")).eq(identifier)).first(); if (appPersistent != null) { return appPersistent.getOpenCount(); } else { return 0; } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/model/Grid.java ================================================ package nickrout.lenslauncher.model; /** * Created by nickrout on 2016/04/02. */ public class Grid { private int mItemCount; private int mItemCountHorizontal; private int mItemCountVertical; private float mItemSize; private float mSpacingHorizontal; private float mSpacingVertical; public Grid() {} public int getItemCount() { return mItemCount; } public void setItemCount(int itemCount) { mItemCount = itemCount; } public int getItemCountHorizontal() { return mItemCountHorizontal; } public void setItemCountHorizontal(int itemCountHorizontal) { mItemCountHorizontal = itemCountHorizontal; } public int getItemCountVertical() { return mItemCountVertical; } public void setItemCountVertical(int itemCountVertical) { mItemCountVertical = itemCountVertical; } public float getItemSize() { return mItemSize; } public void setItemSize(float itemSize) { mItemSize = itemSize; } public float getSpacingHorizontal() { return mSpacingHorizontal; } public void setSpacingHorizontal(float spacingHorizontal) { mSpacingHorizontal = spacingHorizontal; } public float getSpacingVertical() { return mSpacingVertical; } public void setSpacingVertical(float spacingVertical) { mSpacingVertical = spacingVertical; } @Override public String toString() { return "Grid{" + "mItemCount=" + mItemCount + ", mItemCountHorizontal=" + mItemCountHorizontal + ", mItemCountVertical=" + mItemCountVertical + ", mItemSize=" + mItemSize + ", mSpacingHorizontal=" + mSpacingHorizontal + ", mSpacingVertical=" + mSpacingVertical + '}'; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/AboutActivity.java ================================================ package nickrout.lenslauncher.ui; import android.animation.Animator; import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.CollapsingToolbarLayout; import android.support.v4.content.ContextCompat; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.method.LinkMovementMethod; import android.view.MenuItem; import android.view.View; import android.view.ViewAnimationUtils; import android.widget.ImageView; import android.widget.TextView; import java.util.Observable; import java.util.Observer; import butterknife.BindView; import butterknife.ButterKnife; import nickrout.lenslauncher.R; import nickrout.lenslauncher.background.NightModeObservable; /** * Created by nickrout on 2016/04/06. */ public class AboutActivity extends BaseActivity implements Observer { private static final String TAG = "AboutActivity"; @BindView(R.id.text_view_about) TextView mTextViewAbout; @BindView(R.id.backdrop) ImageView mImageAbout; @BindView(R.id.collapsing_toolbar) CollapsingToolbarLayout mCollapsingToolbar; @BindView(R.id.toolbar) Toolbar mToolbar; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); ButterKnife.bind(this); setSupportActionBar(mToolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); mCollapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(this, R.color.colorTransparent)); setupViews(); mImageAbout.postDelayed(new Runnable() { @Override public void run() { circularRevealAboutImage(); } }, 150); NightModeObservable.getInstance().addObserver(this); } private void circularRevealAboutImage() { if (mImageAbout != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { int cx = mImageAbout.getWidth() / 2; int cy = mImageAbout.getHeight() / 2; float finalRadius = (float) Math.hypot(cx, cy); Animator anim = ViewAnimationUtils.createCircularReveal(mImageAbout, cx, cy, 0, finalRadius); mImageAbout.setVisibility(View.VISIBLE); anim.start(); } else { mImageAbout.setVisibility(View.VISIBLE); } } } private void setupViews() { mTextViewAbout.setText(Html.fromHtml(getString(R.string.about))); mTextViewAbout.setMovementMethod(LinkMovementMethod.getInstance()); } @Override public void onBackPressed() { finish(); } @Override public void finish() { super.finish(); overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; } return super.onOptionsItemSelected(item); } @Override public void update(Observable observable, Object o) { if (observable instanceof NightModeObservable) { updateNightMode(); } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/AppsFragment.java ================================================ package nickrout.lenslauncher.ui; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import butterknife.BindView; import butterknife.ButterKnife; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import nickrout.lenslauncher.R; import nickrout.lenslauncher.adapter.AppRecyclerAdapter; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.util.AppSorter; import nickrout.lenslauncher.AppsSingleton; import nickrout.lenslauncher.background.BroadcastReceivers; import nickrout.lenslauncher.util.Settings; /** * Created by nicholasrout on 2016/06/08. */ public class AppsFragment extends Fragment implements SettingsActivity.AppsInterface { private static final String TAG = "AppsFragment"; @BindView(R.id.recycler_apps) RecyclerView mRecycler; @BindView(R.id.progress_apps) MaterialProgressBar mProgress; private Settings mSettings; private AppRecyclerAdapter mAppRecyclerAdapter; private int mScrolledItemIndex; public AppsFragment() { } public static AppsFragment newInstance() { AppsFragment appsFragment = new AppsFragment(); // Include potential bundle extras here return appsFragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_apps, container, false); ButterKnife.bind(this, view); mSettings = new Settings(getActivity()); setupRecycler(AppsSingleton.getInstance().getApps()); return view; } @Override public void onAttach(Context context) { super.onAttach(context); if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).setAppsInterface(this); } } private void sendEditAppsBroadcast() { if (getActivity() == null) { return; } Intent editAppsIntent = new Intent(getActivity(), BroadcastReceivers.AppsEditedReceiver.class); getActivity().sendBroadcast(editAppsIntent); } private void setupRecycler(ArrayList apps) { if (getActivity() == null || apps.size() == 0) { return; } if (mRecycler.getLayoutManager() != null) { mScrolledItemIndex = ((LinearLayoutManager) mRecycler.getLayoutManager()).findFirstCompletelyVisibleItemPosition(); } mProgress.setVisibility(View.INVISIBLE); mRecycler.setVisibility(View.VISIBLE); mAppRecyclerAdapter = new AppRecyclerAdapter(getActivity(), apps); mRecycler.setAdapter(mAppRecyclerAdapter); mRecycler.setLayoutManager(new GridLayoutManager(getActivity(), getResources().getInteger(R.integer.columns_apps))); mRecycler.setItemAnimator(new DefaultItemAnimator()); mRecycler.scrollToPosition(mScrolledItemIndex); mScrolledItemIndex = 0; } @Override public void onDefaultsReset() { if (mSettings.getSortType() != AppSorter.SortType.values()[Settings.DEFAULT_SORT_TYPE]) { mSettings.save(Settings.KEY_SORT_TYPE, Settings.DEFAULT_SORT_TYPE); sendEditAppsBroadcast(); } } @Override public void onAppsUpdated(ArrayList apps) { setupRecycler(apps); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/BaseActivity.java ================================================ package nickrout.lenslauncher.ui; import android.app.ActivityManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Bundle; import android.support.annotation.LayoutRes; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import nickrout.lenslauncher.R; import nickrout.lenslauncher.util.Settings; /** * Created by nickrout on 2016/04/05. */ public class BaseActivity extends AppCompatActivity { protected Settings mSettings; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { mSettings = new Settings(this); if (savedInstanceState == null) { updateNightMode(); } super.onCreate(savedInstanceState); } @Override public void setContentView(@LayoutRes int layoutResID) { super.setContentView(layoutResID); setTaskDescription(); } private void setTaskDescription() { if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Bitmap appIconBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); ActivityManager.TaskDescription taskDescription = new ActivityManager.TaskDescription( getString(R.string.app_name), appIconBitmap, ContextCompat.getColor(getBaseContext(), R.color.colorPrimaryDark)); setTaskDescription(taskDescription); } } protected void updateNightMode() { if (mSettings == null) { mSettings = new Settings(this); } getDelegate().setLocalNightMode(mSettings.getNightMode()); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/FakeLauncherActivity.java ================================================ package nickrout.lenslauncher.ui; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import nickrout.lenslauncher.R; public class FakeLauncherActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fake_launcher); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/HomeActivity.java ================================================ package nickrout.lenslauncher.ui; import android.annotation.TargetApi; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.view.View; import android.view.Window; import android.view.WindowManager; import java.util.ArrayList; import java.util.Observable; import java.util.Observer; import butterknife.BindView; import butterknife.ButterKnife; import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import nickrout.lenslauncher.R; import nickrout.lenslauncher.background.BackgroundChangedObservable; import nickrout.lenslauncher.background.NightModeObservable; import nickrout.lenslauncher.background.VisibilityChangedObservable; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.model.AppPersistent; import nickrout.lenslauncher.AppsSingleton; import nickrout.lenslauncher.background.LoadedObservable; /** * Created by nickrout on 2016/04/02. */ public class HomeActivity extends BaseActivity implements Observer { private final static String TAG = "HomeActivity"; @BindView(R.id.lens_view_apps) LensView mLensView; @BindView(R.id.progress_home) MaterialProgressBar mProgress; private PackageManager mPackageManager; private ArrayList mApps; private ArrayList mAppIcons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); ButterKnife.bind(this); mPackageManager = getPackageManager(); mLensView.setPackageManager(mPackageManager); mLensView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); assignApps(AppsSingleton.getInstance().getApps(), AppsSingleton.getInstance().getAppIcons()); LoadedObservable.getInstance().addObserver(this); VisibilityChangedObservable.getInstance().addObserver(this); BackgroundChangedObservable.getInstance().addObserver(this); NightModeObservable.getInstance().addObserver(this); } @Override protected void onResume() { super.onResume(); setupTransparentSystemBarsForLollipop(); } /** * Sets up transparent navigation and status bars in Lollipop. * This method is a no-op for other platform versions. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void setupTransparentSystemBarsForLollipop() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.getAttributes().systemUiVisibility |= (View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); window.setNavigationBarColor(Color.TRANSPARENT); } } private void setBackground() { mLensView.invalidate(); } private void assignApps(ArrayList apps, ArrayList appIcons) { if (apps.size() == 0 || appIcons.size() == 0) { return; } mProgress.setVisibility(View.INVISIBLE); mLensView.setVisibility(View.VISIBLE); mApps = apps; mAppIcons = appIcons; removeHiddenApps(); mLensView.setApps(mApps, mAppIcons); } private void removeHiddenApps() { for (int i = 0; i < mApps.size(); i++) { if (!AppPersistent.getAppVisibility( mApps.get(i).getPackageName().toString(), mApps.get(i).getName().toString())) { mApps.remove(i); mAppIcons.remove(i); i--; } } } @Override public void onBackPressed() { // Do Nothing } @Override protected void onDestroy() { LoadedObservable.getInstance().deleteObserver(this); super.onDestroy(); } @Override public void update(Observable observable, Object data) { if (observable instanceof LoadedObservable || observable instanceof VisibilityChangedObservable) { assignApps(AppsSingleton.getInstance().getApps(), AppsSingleton.getInstance().getAppIcons()); } else if (observable instanceof BackgroundChangedObservable) { setBackground(); } else if (observable instanceof NightModeObservable) { updateNightMode(); } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/LensFragment.java ================================================ package nickrout.lenslauncher.ui; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.AppCompatSeekBar; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; import android.widget.TextView; import butterknife.BindView; import butterknife.ButterKnife; import nickrout.lenslauncher.R; import nickrout.lenslauncher.util.Settings; /** * Created by nicholasrout on 2016/06/08. */ public class LensFragment extends Fragment implements SettingsActivity.LensInterface { private static final String TAG = "LensFragment"; @BindView(R.id.lens_view_settings) LensView mLensView; @BindView(R.id.seek_bar_min_icon_size) AppCompatSeekBar mMinIconSize; @BindView(R.id.value_min_icon_size) TextView mValueMinIconSize; @BindView(R.id.seek_bar_distortion_factor) AppCompatSeekBar mDistortionFactor; @BindView(R.id.value_distortion_factor) TextView mValueDistortionFactor; @BindView(R.id.seek_bar_scale_factor) AppCompatSeekBar mScaleFactor; @BindView(R.id.value_scale_factor) TextView mValueScaleFactor; @BindView(R.id.seek_bar_animation_time) AppCompatSeekBar mAnimationTime; @BindView(R.id.value_animation_time) TextView mValueAnimationTime; private Settings mSettings; public LensFragment() { } public static LensFragment newInstance() { LensFragment lensFragment = new LensFragment(); // Include potential bundle extras here return lensFragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_lens, container, false); ButterKnife.bind(this, view); mSettings = new Settings(getActivity()); setupViews(); assignValues(); return view; } @Override public void onAttach(Context context) { super.onAttach(context); if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).setLensInterface(this); } } private void setupViews() { mLensView.setDrawType(LensView.DrawType.CIRCLES); mMinIconSize.setMax(Settings.MAX_ICON_SIZE); mMinIconSize.setOnSeekBarChangeListener(new AppCompatSeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { int appropriateProgress = progress + (int) Settings.MIN_ICON_SIZE; String minIconSize = appropriateProgress + "dp"; mValueMinIconSize.setText(minIconSize); mSettings.save(Settings.KEY_ICON_SIZE, (float) appropriateProgress); mLensView.invalidate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); mDistortionFactor.setMax(Settings.MAX_DISTORTION_FACTOR); mDistortionFactor.setOnSeekBarChangeListener(new AppCompatSeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float appropriateProgress = (float) progress / 2.0f + Settings.MIN_DISTORTION_FACTOR; String distortionFactor = appropriateProgress + ""; mValueDistortionFactor.setText(distortionFactor); mSettings.save(Settings.KEY_DISTORTION_FACTOR, appropriateProgress); mLensView.invalidate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); mScaleFactor.setMax(Settings.MAX_SCALE_FACTOR); mScaleFactor.setOnSeekBarChangeListener(new AppCompatSeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { float appropriateProgress = (float) progress / 5.0f + Settings.MIN_SCALE_FACTOR; String scaleFactor = appropriateProgress + ""; mValueScaleFactor.setText(scaleFactor); mSettings.save(Settings.KEY_SCALE_FACTOR, appropriateProgress); mLensView.invalidate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); mAnimationTime.setMax(Settings.MAX_ANIMATION_TIME); mAnimationTime.setOnSeekBarChangeListener(new AppCompatSeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { long appropriateProgress = (long) progress / 2 + Settings.MIN_ANIMATION_TIME; String animationTime = appropriateProgress + "ms"; mValueAnimationTime.setText(animationTime); mSettings.save(Settings.KEY_ANIMATION_TIME, appropriateProgress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); } private void assignValues() { mMinIconSize.setProgress((int) mSettings.getFloat(Settings.KEY_ICON_SIZE) - (int) Settings.MIN_ICON_SIZE); String minIconSize = (int) mSettings.getFloat(Settings.KEY_ICON_SIZE) + "dp"; mValueMinIconSize.setText(minIconSize); mDistortionFactor.setProgress((int) (2.0f * (mSettings.getFloat(Settings.KEY_DISTORTION_FACTOR) - Settings.MIN_DISTORTION_FACTOR))); String distortionFactor = mSettings.getFloat(Settings.KEY_DISTORTION_FACTOR) + ""; mValueDistortionFactor.setText(distortionFactor); mScaleFactor.setProgress((int) (5.0f * (mSettings.getFloat(Settings.KEY_SCALE_FACTOR) - Settings.MIN_SCALE_FACTOR))); String scaleFactor = mSettings.getFloat(Settings.KEY_SCALE_FACTOR) + ""; mValueScaleFactor.setText(scaleFactor); mAnimationTime.setProgress((int) (2 * (mSettings.getLong(Settings.KEY_ANIMATION_TIME) - Settings.MIN_ANIMATION_TIME))); String animationTime = mSettings.getLong(Settings.KEY_ANIMATION_TIME) + "ms"; mValueAnimationTime.setText(animationTime); } @Override public void onDefaultsReset() { resetToDefault(); assignValues(); } private void resetToDefault() { mSettings.save(Settings.KEY_ICON_SIZE, Settings.DEFAULT_ICON_SIZE); mSettings.save(Settings.KEY_DISTORTION_FACTOR, Settings.DEFAULT_DISTORTION_FACTOR); mSettings.save(Settings.KEY_SCALE_FACTOR, Settings.DEFAULT_SCALE_FACTOR); mSettings.save(Settings.KEY_ANIMATION_TIME, Settings.DEFAULT_ANIMATION_TIME); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/LensView.java ================================================ package nickrout.lenslauncher.ui; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.NinePatchDrawable; import android.os.Build; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Transformation; import java.util.ArrayList; import nickrout.lenslauncher.R; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.model.AppPersistent; import nickrout.lenslauncher.model.Grid; import nickrout.lenslauncher.util.AppUtil; import nickrout.lenslauncher.util.LensCalculator; import nickrout.lenslauncher.util.Settings; /** * Created by nickrout on 2016/04/02. */ public class LensView extends View { private static final String TAG = "LensView"; private Paint mPaintIcons; private Paint mPaintCircles; private Paint mPaintTouchSelection; private Paint mPaintText; private Paint mPaintNewAppTag; private float mTouchX = -Float.MAX_VALUE; private float mTouchY = -Float.MAX_VALUE; private boolean mInsideRect = false; private RectF mRectToSelect = new RectF(0, 0, 0, 0); private boolean mMustVibrate = true; private int mSelectIndex; private ArrayList mApps; private ArrayList mAppIcons; private PackageManager mPackageManager; private float mAnimationMultiplier = 0.0f; private boolean mAnimationHiding = false; private int mNumberOfCircles; private float mTouchSlop; private boolean mMoving; private Settings mSettings; private NinePatchDrawable mWorkspaceBackgroundDrawable; private Rect mInsets = new Rect(0, 0, 0, 0); public enum DrawType { APPS, CIRCLES } private DrawType mDrawType; public void setDrawType(DrawType drawType) { mDrawType = drawType; invalidate(); } public LensView(Context context) { super(context); init(); } public LensView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public LensView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } public void setApps(ArrayList apps, ArrayList appIcons) { mApps = apps; mAppIcons = appIcons; invalidate(); } public void setPackageManager(PackageManager packageManager) { mPackageManager = packageManager; } private void init() { mApps = new ArrayList<>(); mAppIcons = new ArrayList<>(); mDrawType = DrawType.APPS; setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorTransparent)); mSettings = new Settings(getContext()); setupPaints(); mWorkspaceBackgroundDrawable = (NinePatchDrawable) ContextCompat.getDrawable(getContext(), R.drawable.workspace_bg); mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); } @Override protected boolean fitSystemWindows(Rect insets) { mInsets = insets; return true; } private void setupPaints() { mPaintIcons = new Paint(); mPaintIcons.setAntiAlias(true); mPaintIcons.setStyle(Paint.Style.FILL); mPaintIcons.setFilterBitmap(true); mPaintIcons.setDither(true); mPaintCircles = new Paint(); mPaintCircles.setAntiAlias(true); mPaintCircles.setStyle(Paint.Style.FILL); mPaintCircles.setColor(ContextCompat.getColor(getContext(), R.color.colorCircles)); mPaintTouchSelection = new Paint(); mPaintTouchSelection.setAntiAlias(true); mPaintTouchSelection.setStyle(Paint.Style.STROKE); mPaintTouchSelection.setColor(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))); mPaintTouchSelection.setStrokeWidth(getResources().getDimension(R.dimen.stroke_width_touch_selection)); mPaintText = new Paint(); mPaintText.setAntiAlias(true); mPaintText.setStyle(Paint.Style.FILL); mPaintText.setColor(ContextCompat.getColor(getContext(), R.color.colorWhite)); mPaintText.setTextSize(getResources().getDimension(R.dimen.text_size_lens)); mPaintText.setTextAlign(Paint.Align.CENTER); mPaintText.setTypeface(Typeface.createFromAsset(getContext().getAssets(),"fonts/RobotoCondensed-Regular.ttf")); mPaintNewAppTag = new Paint(); mPaintNewAppTag.setAntiAlias(true); mPaintNewAppTag.setStyle(Paint.Style.FILL); mPaintNewAppTag.setColor(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))); mPaintNewAppTag.setDither(true); mPaintNewAppTag.setShadowLayer(getResources().getDimension(R.dimen.shadow_text), getResources().getDimension(R.dimen.shadow_text), getResources().getDimension(R.dimen.shadow_text), ContextCompat.getColor(getContext(), R.color.colorShadow)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (mDrawType == DrawType.APPS) { if (mSettings.getString(Settings.KEY_BACKGROUND).equals("Color")) { canvas.drawColor(Color.parseColor(mSettings.getString(Settings.KEY_BACKGROUND_COLOR))); } mPaintNewAppTag.setColor(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))); drawWorkspaceBackground(canvas); if (mApps != null) { drawGrid(canvas, mApps.size()); } if (mSettings.getBoolean(Settings.KEY_SHOW_TOUCH_SELECTION)) { drawTouchSelection(canvas); } } else if (mDrawType == DrawType.CIRCLES) { mNumberOfCircles = 100; mTouchX = getWidth() / 2; mTouchY = getHeight() / 2; drawGrid(canvas, mNumberOfCircles); } } @Override public boolean onTouchEvent(MotionEvent event) { if (mDrawType == DrawType.APPS) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { if (event.getX() < 0.0f) { mTouchX = 0.0f; } else { mTouchX = event.getX(); } if (event.getY() < 0.0f) { mTouchY = 0.0f; } else { mTouchY = event.getY(); } mSelectIndex = -1; mMoving = false; invalidate(); return true; } case MotionEvent.ACTION_MOVE: { if (!mMoving && Math.sqrt(Math.pow(event.getX() - mTouchX, 2) + Math.pow(event.getY() - mTouchY, 2)) > mTouchSlop) { mMoving = true; LensAnimation lensShowAnimation = new LensAnimation(true); startAnimation(lensShowAnimation); } if (!mMoving) { return true; } if (event.getX() < 0.0f) { mTouchX = 0.0f; } else { mTouchX = event.getX(); } if (event.getY() < 0.0f) { mTouchY = 0.0f; } else { mTouchY = event.getY(); } invalidate(); return true; } case MotionEvent.ACTION_UP: { performLaunchVibration(); if (mMoving) { LensAnimation lensHideAnimation = new LensAnimation(false); startAnimation(lensHideAnimation); mMoving = false; } else { launchApp(); } return true; } default: { return super.onTouchEvent(event); } } } return super.onTouchEvent(event); } private void drawWorkspaceBackground(Canvas canvas) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Rect rect = new Rect(0, 0, getWidth(), getHeight()); mWorkspaceBackgroundDrawable.setBounds(rect); mWorkspaceBackgroundDrawable.draw(canvas); } } private void drawTouchSelection(Canvas canvas) { canvas.drawCircle(mTouchX, mTouchY, getResources().getDimension(R.dimen.radius_touch_selection), mPaintTouchSelection); } private void drawGrid(Canvas canvas, int itemCount) { Grid grid = LensCalculator.calculateGrid( getContext(), getWidth() - (mInsets.left + mInsets.right), getHeight() - (mInsets.top + mInsets.bottom), itemCount); mInsideRect = false; int selectIndex = -1; mRectToSelect = null; for (float y = 0.0f; y < (float) grid.getItemCountVertical(); y += 1.0f) { for (float x = 0.0f; x < (float) grid.getItemCountHorizontal(); x += 1.0f) { int currentItem = (int) (y * ((float) grid.getItemCountHorizontal()) + (x + 1.0f)); int currentIndex = currentItem - 1; if (currentItem <= grid.getItemCount() || mDrawType == DrawType.CIRCLES) { RectF rect = new RectF(); rect.left = mInsets.left + (x + 1.0f) * grid.getSpacingHorizontal() + x * grid.getItemSize(); rect.top = mInsets.top + (y + 1.0f) * grid.getSpacingVertical() + y * grid.getItemSize(); rect.right = rect.left + grid.getItemSize(); rect.bottom = rect.top + grid.getItemSize(); float animationMultiplier; if (mDrawType == DrawType.APPS) { animationMultiplier = mAnimationMultiplier; } else { animationMultiplier = 1.0f; } if (mTouchX >= 0 && mTouchY >= 0) { float shiftedCenterX = LensCalculator.shiftPoint(getContext(), mTouchX, rect.centerX(), getWidth(), animationMultiplier); float shiftedCenterY = LensCalculator.shiftPoint(getContext(), mTouchY, rect.centerY(), getHeight(), animationMultiplier); float scaledCenterX = LensCalculator.scalePoint(getContext(), mTouchX, rect.centerX(), rect.width(), getWidth(), animationMultiplier); float scaledCenterY = LensCalculator.scalePoint(getContext(), mTouchY, rect.centerY(), rect.height(), getHeight(), animationMultiplier); float newSize = LensCalculator.calculateSquareScaledSize(scaledCenterX, shiftedCenterX, scaledCenterY, shiftedCenterY); if (mSettings.getFloat(Settings.KEY_DISTORTION_FACTOR) > 0.0f && mSettings.getFloat(Settings.KEY_SCALE_FACTOR) > 0.0f) { rect = LensCalculator.calculateRect(shiftedCenterX, shiftedCenterY, newSize); } else if (mSettings.getFloat(Settings.KEY_DISTORTION_FACTOR) > 0.0f && mSettings.getFloat(Settings.KEY_SCALE_FACTOR) == 0.0f) { rect = LensCalculator.calculateRect(shiftedCenterX, shiftedCenterY, rect.width()); } if (LensCalculator.isInsideRect(mTouchX, mTouchY, rect)) { mInsideRect = true; selectIndex = currentIndex; mRectToSelect = rect; } } if (mDrawType == DrawType.APPS) { drawAppIcon(canvas, rect, currentIndex); } else if (mDrawType == DrawType.CIRCLES) { drawCircle(canvas, rect); } } } } if (selectIndex >= 0) { if (selectIndex != mSelectIndex) { mMustVibrate = true; } else { mMustVibrate = false; } } else { mMustVibrate = false; } if (!mAnimationHiding) { mSelectIndex = selectIndex; } if (mDrawType == DrawType.APPS) { performHoverVibration(); } if (mRectToSelect != null && mDrawType == DrawType.APPS && mApps != null && mSelectIndex >= 0) { drawAppName(canvas, mRectToSelect); } } private void drawAppIcon(Canvas canvas, RectF rect, int index) { if (index < mAppIcons.size()) { Bitmap appIcon = mAppIcons.get(index); Rect src = new Rect(0, 0, appIcon.getWidth(), appIcon.getHeight()); canvas.drawBitmap(appIcon, src, rect, mPaintIcons); /** * Check if the app was installed Settings.SHOW_NEW_APP_TAG_DURATION ago, and if it has been opened since. * If not, drawNewAppTag() */ if ((mApps.get(index).getInstallDate() >= (System.currentTimeMillis() - Settings.SHOW_NEW_APP_TAG_DURATION) && (AppPersistent.getAppOpenCount( mApps.get(index).getPackageName().toString(), mApps.get(index).getName().toString()) == 0))) { drawNewAppTag(canvas, rect); } } } private void drawCircle(Canvas canvas, RectF rect) { canvas.drawCircle(rect.centerX(), rect.centerY(), rect.width() / 2.0f, mPaintCircles); } private void drawAppName(Canvas canvas, RectF rect) { if (mSettings.getBoolean(Settings.KEY_SHOW_NAME_APP_HOVER) && mMoving) { canvas.drawText((String) mApps.get(mSelectIndex).getLabel(), rect.centerX(), rect.top - getResources().getDimension(R.dimen.margin_lens_text), mPaintText); } } private void drawNewAppTag(Canvas canvas, RectF rect) { if (mSettings.getBoolean(Settings.KEY_SHOW_NEW_APP_TAG)) { canvas.drawCircle(rect.centerX(), rect.bottom + getResources().getDimension(R.dimen.margin_new_app_tag), getResources().getDimension(R.dimen.radius_new_app_tag), mPaintNewAppTag); } } private void performHoverVibration() { if (mInsideRect) { if (mMustVibrate) { if (mSettings.getBoolean(Settings.KEY_VIBRATE_APP_HOVER) && !mAnimationHiding) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } mMustVibrate = false; } } else { mMustVibrate = true; } } private void performLaunchVibration() { if (mInsideRect) { if (mSettings.getBoolean(Settings.KEY_VIBRATE_APP_LAUNCH)) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); } } } private void launchApp() { if (mPackageManager != null && mApps != null && mSelectIndex >= 0) { AppUtil.launchComponent( getContext(), (String) mApps.get(mSelectIndex).getPackageName(), (String) mApps.get(mSelectIndex).getName(), this, mRectToSelect == null ? null : new Rect((int) mRectToSelect.left, (int) mRectToSelect.top, (int) mRectToSelect.right, (int) mRectToSelect.bottom)); } } private class LensAnimation extends Animation { private boolean mShow; public LensAnimation(boolean show) { mShow = show; setInterpolator(new AccelerateDecelerateInterpolator()); setDuration(mSettings.getLong(Settings.KEY_ANIMATION_TIME)); setAnimationListener(new AnimationListener() { @Override public void onAnimationStart(Animation animation) { if (!mShow) { mAnimationHiding = true; mPaintText.clearShadowLayer(); } else { mAnimationHiding = false; } } @Override public void onAnimationEnd(Animation animation) { if (!mShow) { launchApp(); mTouchX = -Float.MAX_VALUE; mTouchY = -Float.MAX_VALUE; mAnimationHiding = false; } else { mPaintText.setShadowLayer( getResources().getDimension(R.dimen.shadow_text), getResources().getDimension(R.dimen.shadow_text), getResources().getDimension(R.dimen.shadow_text), ContextCompat.getColor(getContext(), R.color.colorShadow)); } } @Override public void onAnimationRepeat(Animation animation) { } }); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { super.applyTransformation(interpolatedTime, t); if (mShow) { mAnimationMultiplier = interpolatedTime; mPaintTouchSelection.setColor(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))); mPaintTouchSelection.setAlpha((int) (255.0f * interpolatedTime)); mPaintText.setAlpha((int) (255.0f * interpolatedTime)); } else { mAnimationMultiplier = 1.0f - interpolatedTime; mPaintTouchSelection.setColor(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))); mPaintTouchSelection.setAlpha((int) (255.0f * (1.0f - interpolatedTime))); mPaintText.setAlpha((int) (255.0f * (1.0f - interpolatedTime))); } postInvalidate(); } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/SettingsActivity.java ================================================ package nickrout.lenslauncher.ui; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import com.afollestad.materialdialogs.MaterialDialog; import com.afollestad.materialdialogs.color.ColorChooserDialog; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Observable; import java.util.Observer; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import nickrout.lenslauncher.R; import nickrout.lenslauncher.background.NightModeObservable; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.util.AppSorter; import nickrout.lenslauncher.AppsSingleton; import nickrout.lenslauncher.background.BroadcastReceivers; import nickrout.lenslauncher.util.IconPackManager; import nickrout.lenslauncher.util.LauncherUtil; import nickrout.lenslauncher.background.LoadedObservable; import nickrout.lenslauncher.util.NightModeUtil; import nickrout.lenslauncher.util.Settings; /** * Created by nicholasrout on 2016/06/07. */ public class SettingsActivity extends BaseActivity implements Observer, ColorChooserDialog.ColorCallback { private static final String TAG = "SettingsActivity"; private static final String COLOR_TAG_BACKGROUND = "BackgroundColor"; private static final String COLOR_TAG_HIGHLIGHT = "HighlightColor"; @BindView(R.id.toolbar) Toolbar mToolbar; @BindView(R.id.tabs) TabLayout mTabLayout; @BindView(R.id.viewpager) ViewPager mViewPager; @BindView(R.id.fab_sort) FloatingActionButton mSortFab; @OnClick(R.id.fab_sort) public void onSortClicked() { showSortTypeDialog(); } private FragmentPagerAdapter mPagerAdapter; private ArrayList mApps; private MaterialDialog mSortTypeDialog; private MaterialDialog mIconPackDialog; private MaterialDialog mNightModeDialog; private MaterialDialog mBackgroundDialog; private ColorChooserDialog mBackgroundColorDialog; private ColorChooserDialog mHighlightColorDialog; public interface LensInterface { void onDefaultsReset(); } private LensInterface mLensInterface; public void setLensInterface(LensInterface lensInterface) { mLensInterface = lensInterface; } public interface AppsInterface { void onDefaultsReset(); void onAppsUpdated(ArrayList apps); } private AppsInterface mAppsInterface; public void setAppsInterface(AppsInterface appsInterface) { mAppsInterface = appsInterface; } public interface SettingsInterface { void onDefaultsReset(); void onValuesUpdated(); } private SettingsInterface mSettingsInterface; public void setSettingsInterface(SettingsInterface settingsInterface) { mSettingsInterface = settingsInterface; } @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_settings); ButterKnife.bind(this); mSortFab.hide(); setSupportActionBar(mToolbar); mPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager(), SettingsActivity.this); mViewPager.setAdapter(mPagerAdapter); mTabLayout.setupWithViewPager(mViewPager); mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { switch (position) { case 1: mSortFab.show(); break; default: mSortFab.hide(); break; } } @Override public void onPageScrollStateChanged(int state) { } }); mApps = AppsSingleton.getInstance().getApps(); LoadedObservable.getInstance().addObserver(this); NightModeObservable.getInstance().addObserver(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_settings, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_item_show_apps: if (LauncherUtil.isLauncherDefault(getApplication())) { Intent homeIntent = new Intent(Intent.ACTION_MAIN); homeIntent.addCategory(Intent.CATEGORY_HOME); startActivity(homeIntent); } else { Intent homeIntent = new Intent(SettingsActivity.this, HomeActivity.class); startActivity(homeIntent); } overridePendingTransition(R.anim.fade_in, R.anim.fade_out); return true; case R.id.menu_item_about: Intent aboutIntent = new Intent(SettingsActivity.this, AboutActivity.class); startActivity(aboutIntent); overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right); return true; case R.id.menu_item_reset_default_settings: switch (mViewPager.getCurrentItem()) { case 0: if (mLensInterface != null) { mLensInterface.onDefaultsReset(); } break; case 1: if (mAppsInterface != null) { mAppsInterface.onDefaultsReset(); } break; case 2: if (mSettingsInterface != null) { mSettingsInterface.onDefaultsReset(); } break; } Snackbar.make(mToolbar, getString(R.string.snackbar_reset_successful), Snackbar.LENGTH_LONG).show(); return true; default: return super.onOptionsItemSelected(item); } } @Override public void update(Observable observable, Object data) { if (observable instanceof LoadedObservable) { mApps = AppsSingleton.getInstance().getApps(); if (mAppsInterface != null) { mAppsInterface.onAppsUpdated(mApps); } } else if (observable instanceof NightModeObservable) { updateNightMode(); } } private void sendUpdateAppsBroadcast() { Intent refreshAppsIntent = new Intent(SettingsActivity.this, BroadcastReceivers.AppsUpdatedReceiver.class); sendBroadcast(refreshAppsIntent); } private void sendEditAppsBroadcast() { Intent editAppsIntent = new Intent(SettingsActivity.this, BroadcastReceivers.AppsEditedReceiver.class); sendBroadcast(editAppsIntent); } private void sendBackgroundChangedBroadcast() { Intent changeBackgroundIntent = new Intent(SettingsActivity.this, BroadcastReceivers.BackgroundChangedReceiver.class); sendBroadcast(changeBackgroundIntent); } public void sendNightModeBroadcast() { Intent nightModeIntent = new Intent(SettingsActivity.this, BroadcastReceivers.NightModeReceiver.class); sendBroadcast(nightModeIntent); } private void showSortTypeDialog() { final List sortTypes = new ArrayList<>(EnumSet.allOf(AppSorter.SortType.class)); final List sortTypeStrings = new ArrayList<>(); for (int i = 0; i < sortTypes.size(); i++) { sortTypeStrings.add(getApplicationContext().getString(sortTypes.get(i).getDisplayNameResId())); } AppSorter.SortType selectedSortType = mSettings.getSortType(); int selectedIndex = sortTypes.indexOf(selectedSortType); mSortTypeDialog = new MaterialDialog.Builder(SettingsActivity.this) .title(R.string.setting_sort_apps) .items(sortTypeStrings) .alwaysCallSingleChoiceCallback() .itemsCallbackSingleChoice(selectedIndex, new MaterialDialog.ListCallbackSingleChoice() { @Override public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) { mSettings.save(sortTypes.get(which)); sendEditAppsBroadcast(); return true; } }) .show(); } public void showIconPackDialog() { final ArrayList availableIconPacks = new IconPackManager().getAvailableIconPacksWithIcons(true, getApplication()); final ArrayList iconPackNames = new ArrayList<>(); iconPackNames.add(getString(R.string.setting_default_icon_pack)); for (int i = 0; i < availableIconPacks.size(); i++) { if (iconPackNames.size() > 0 && !iconPackNames.contains(availableIconPacks.get(i).mName)) { iconPackNames.add(availableIconPacks.get(i).mName); } } String selectedPackageName = mSettings.getString(Settings.KEY_ICON_PACK_LABEL_NAME); int selectedIndex = iconPackNames.indexOf(selectedPackageName); mIconPackDialog = new MaterialDialog.Builder(SettingsActivity.this) .title(R.string.setting_icon_pack) .items(iconPackNames) .alwaysCallSingleChoiceCallback() .itemsCallbackSingleChoice(selectedIndex, new MaterialDialog.ListCallbackSingleChoice() { @Override public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) { mSettings.save(Settings.KEY_ICON_PACK_LABEL_NAME, iconPackNames.get(which)); if (mSettingsInterface != null) { mSettingsInterface.onValuesUpdated(); } sendUpdateAppsBroadcast(); return true; } }) .show(); } public void showHomeLauncherChooser() { LauncherUtil.resetPreferredLauncherAndOpenChooser(getApplicationContext()); } public void showNightModeChooser() { String[] availableNightModes = getResources().getStringArray(R.array.night_modes); final ArrayList nightModes = new ArrayList<>(); for (int i = 0; i < availableNightModes.length; i++) { nightModes.add(availableNightModes[i]); } String selectedNightMode = NightModeUtil.getNightModeDisplayName(mSettings.getNightMode()); int selectedIndex = nightModes.indexOf(selectedNightMode); mNightModeDialog = new MaterialDialog.Builder(SettingsActivity.this) .title(R.string.setting_night_mode) .items(R.array.night_modes) .alwaysCallSingleChoiceCallback() .itemsCallbackSingleChoice(selectedIndex, new MaterialDialog.ListCallbackSingleChoice() { @Override public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) { String selection = nightModes.get(which); mSettings.save(Settings.KEY_NIGHT_MODE, NightModeUtil.getNightModeFromDisplayName(selection)); sendNightModeBroadcast(); if (mSettingsInterface != null) { mSettingsInterface.onValuesUpdated(); } dismissBackgroundDialog(); return true; } }) .show(); } public void showBackgroundDialog() { String[] availableBackgrounds = getResources().getStringArray(R.array.backgrounds); final ArrayList backgroundNames = new ArrayList<>(); for (int i = 0; i < availableBackgrounds.length; i++) { backgroundNames.add(availableBackgrounds[i]); } String selectedBackground = mSettings.getString(Settings.KEY_BACKGROUND); int selectedIndex = backgroundNames.indexOf(selectedBackground); mBackgroundDialog = new MaterialDialog.Builder(SettingsActivity.this) .title(R.string.setting_background) .items(R.array.backgrounds) .alwaysCallSingleChoiceCallback() .itemsCallbackSingleChoice(selectedIndex, new MaterialDialog.ListCallbackSingleChoice() { @Override public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) { String selection = backgroundNames.get(which); if (selection.equals("Wallpaper")) { mSettings.save(Settings.KEY_BACKGROUND, selection); sendBackgroundChangedBroadcast(); if (mSettingsInterface != null) { mSettingsInterface.onValuesUpdated(); } dismissBackgroundDialog(); showWallpaperPicker(); } else if (selection.equals("Color")) { dismissBackgroundDialog(); showBackgroundColorDialog(); } return true; } }) .show(); } public void showWallpaperPicker() { Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER); startActivity(Intent.createChooser(intent, "Select Wallpaper")); } public void showBackgroundColorDialog() { mBackgroundColorDialog = new ColorChooserDialog.Builder(this, R.string.setting_background_color) .titleSub(R.string.setting_background_color) .accentMode(false) .doneButton(com.afollestad.materialdialogs.commons.R.string.md_done_label) .cancelButton(com.afollestad.materialdialogs.commons.R.string.md_cancel_label) .backButton(com.afollestad.materialdialogs.commons.R.string.md_back_label) .preselect(Color.parseColor(mSettings.getString(Settings.KEY_BACKGROUND_COLOR))) .dynamicButtonColor(false) .allowUserColorInputAlpha(false) .tag(COLOR_TAG_BACKGROUND) .show(this); } public void showHighlightColorDialog() { mHighlightColorDialog = new ColorChooserDialog.Builder(this, R.string.setting_highlight_color) .titleSub(R.string.setting_highlight_color) .accentMode(true) .doneButton(com.afollestad.materialdialogs.commons.R.string.md_done_label) .cancelButton(com.afollestad.materialdialogs.commons.R.string.md_cancel_label) .backButton(com.afollestad.materialdialogs.commons.R.string.md_back_label) .preselect(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))) .dynamicButtonColor(false) .allowUserColorInputAlpha(false) .tag(COLOR_TAG_HIGHLIGHT) .show(this); } @Override public void onColorSelection(@NonNull ColorChooserDialog dialog, @ColorInt int selectedColor) { String hexColor = String.format("#%06X", selectedColor); if (dialog.tag().equals(COLOR_TAG_BACKGROUND)) { mSettings.save(Settings.KEY_BACKGROUND, "Color"); mSettings.save(Settings.KEY_BACKGROUND_COLOR, hexColor); sendBackgroundChangedBroadcast(); } else if (dialog.tag().equals(COLOR_TAG_HIGHLIGHT)) { mSettings.save(Settings.KEY_HIGHLIGHT_COLOR, hexColor); } if (mSettingsInterface != null) { mSettingsInterface.onValuesUpdated(); } } @Override public void onColorChooserDismissed(@NonNull ColorChooserDialog dialog) { } private void dismissSortTypeDialog() { if (mSortTypeDialog != null && mSortTypeDialog.isShowing()) { mSortTypeDialog.dismiss(); } } private void dismissIconPackDialog() { if (mIconPackDialog != null && mIconPackDialog.isShowing()) { mIconPackDialog.dismiss(); } } private void dismissNightModeDialog() { if (mNightModeDialog != null && mNightModeDialog.isShowing()) { mNightModeDialog.dismiss(); } } private void dismissBackgroundDialog() { if (mBackgroundDialog != null && mBackgroundDialog.isShowing()) { mBackgroundDialog.dismiss(); } } private void dismissAllDialogs() { dismissSortTypeDialog(); dismissIconPackDialog(); dismissNightModeDialog(); dismissBackgroundDialog(); // Color dialogs do not need to be dismissed } @Override protected void onDestroy() { dismissAllDialogs(); LoadedObservable.getInstance().deleteObserver(this); super.onDestroy(); } @Override public void onBackPressed() { finish(); } @Override public void finish() { super.finish(); overridePendingTransition(R.anim.fade_in, R.anim.fade_out); } private class FragmentPagerAdapter extends FragmentStatePagerAdapter { private static final int NUM_PAGES = 3; private Context mContext; public FragmentPagerAdapter(FragmentManager fragmentManager, Context context) { super(fragmentManager); mContext = context; } @Override public Fragment getItem(int position) { switch (position) { case 0: return LensFragment.newInstance(); case 1: return AppsFragment.newInstance(); case 2: return SettingsFragment.newInstance(); } return new Fragment(); } @Override public int getCount() { return NUM_PAGES; } @Override public CharSequence getPageTitle(int position) { switch (position) { case 0: return mContext.getResources().getString(R.string.tab_lens); case 1: return mContext.getResources().getString(R.string.tab_apps); case 2: return mContext.getResources().getString(R.string.tab_settings); } return super.getPageTitle(position); } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/ui/SettingsFragment.java ================================================ package nickrout.lenslauncher.ui; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.ImageView; import android.widget.TextView; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import nickrout.lenslauncher.R; import nickrout.lenslauncher.util.LauncherUtil; import nickrout.lenslauncher.util.NightModeUtil; import nickrout.lenslauncher.util.Settings; /** * Created by nicholasrout on 2016/06/08. */ public class SettingsFragment extends Fragment implements SettingsActivity.SettingsInterface { private static final String TAG = "SettingsFragment"; @OnClick(R.id.layout_home_launcher) public void onHomeLauncherClick() { showHomeLauncherChooser(); } @BindView(R.id.text_view_selected_home_launcher) TextView mHomeLauncherTextView; @OnClick(R.id.layout_icon_pack) public void onIconPackClick() { showIconPackDialog(); } @BindView(R.id.text_view_selected_icon_pack) TextView mIconPackTextView; @OnClick(R.id.layout_night_mode) public void onNightModeClick() { showNightModeChooser(); } @BindView(R.id.text_view_selected_night_mode) TextView mNightModeTextView; @OnClick(R.id.layout_background) public void onBackgroundClick() { showBackgroundDialog(); } @BindView(R.id.text_view_selected_background) TextView mBackgroundTextView; @BindView(R.id.image_view_selected_background_color) ImageView mBackgroundColorImageView; @OnClick(R.id.layout_highlight_color) public void onHighlightColorClick() { showHighlightColorDialog(); } @BindView(R.id.text_view_selected_highlight_color) TextView mHighlightColorTextView; @BindView(R.id.image_view_selected_highlight_color) ImageView mHighlightColorImageView; @BindView(R.id.switch_vibrate_app_hover) SwitchCompat mVibrateAppHover; @BindView(R.id.switch_vibrate_app_launch) SwitchCompat mVibrateAppLaunch; @BindView(R.id.switch_show_name_app_hover) SwitchCompat mShowNameAppHover; @BindView(R.id.switch_show_new_app_tag) SwitchCompat mShowNewAppTag; @BindView(R.id.switch_show_touch_selection) SwitchCompat mShowTouchSelection; private Settings mSettings; public SettingsFragment() { } public static SettingsFragment newInstance() { SettingsFragment settingsFragment = new SettingsFragment(); // Include potential bundle extras here return settingsFragment; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_settings, container, false); ButterKnife.bind(this, view); mSettings = new Settings(getActivity()); setupViews(); assignValues(); return view; } @Override public void onAttach(Context context) { super.onAttach(context); if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).setSettingsInterface(this); } } private void setupViews() { mVibrateAppHover.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mSettings.save(Settings.KEY_VIBRATE_APP_HOVER, isChecked); } }); mVibrateAppLaunch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mSettings.save(Settings.KEY_VIBRATE_APP_LAUNCH, isChecked); } }); mShowNameAppHover.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mSettings.save(Settings.KEY_SHOW_NAME_APP_HOVER, isChecked); } }); mShowNewAppTag.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mSettings.save(Settings.KEY_SHOW_NEW_APP_TAG, isChecked); } }); mShowTouchSelection.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mSettings.save(Settings.KEY_SHOW_TOUCH_SELECTION, isChecked); } }); } private void assignValues() { mIconPackTextView.setText(mSettings.getString(Settings.KEY_ICON_PACK_LABEL_NAME)); String highlightColor = "#" + mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR).substring(3); String homeLauncher = ""; if (getActivity() != null) { homeLauncher = LauncherUtil.getHomeLauncherName(getActivity().getApplication()); } mHomeLauncherTextView.setText(homeLauncher); mNightModeTextView.setText(NightModeUtil.getNightModeDisplayName(mSettings.getNightMode())); if (mSettings.getString(Settings.KEY_BACKGROUND).equals("Color")) { String backgroundColor = "#" + mSettings.getString(Settings.KEY_BACKGROUND_COLOR).substring(3); mBackgroundTextView.setText(backgroundColor); mBackgroundColorImageView.setVisibility(View.VISIBLE); GradientDrawable backgroundColorDrawable = new GradientDrawable(); backgroundColorDrawable.setColor(Color.parseColor(mSettings.getString(Settings.KEY_BACKGROUND_COLOR))); backgroundColorDrawable.setCornerRadius(getResources().getDimension(R.dimen.radius_highlight_color_switch)); mBackgroundColorImageView.setImageDrawable(backgroundColorDrawable); } else { mBackgroundTextView.setText(mSettings.getString(Settings.KEY_BACKGROUND)); mBackgroundColorImageView.setVisibility(View.GONE); } mHighlightColorTextView.setText(highlightColor); GradientDrawable highlightColorDrawable = new GradientDrawable(); highlightColorDrawable.setColor(Color.parseColor(mSettings.getString(Settings.KEY_HIGHLIGHT_COLOR))); highlightColorDrawable.setCornerRadius(getResources().getDimension(R.dimen.radius_highlight_color_switch)); mHighlightColorImageView.setImageDrawable(highlightColorDrawable); mVibrateAppHover.setChecked(mSettings.getBoolean(Settings.KEY_VIBRATE_APP_HOVER)); mVibrateAppLaunch.setChecked(mSettings.getBoolean(Settings.KEY_VIBRATE_APP_LAUNCH)); mShowNameAppHover.setChecked(mSettings.getBoolean(Settings.KEY_SHOW_NAME_APP_HOVER)); mShowNewAppTag.setChecked(mSettings.getBoolean(Settings.KEY_SHOW_NEW_APP_TAG)); mShowTouchSelection.setChecked(mSettings.getBoolean(Settings.KEY_SHOW_TOUCH_SELECTION)); } private void showIconPackDialog() { if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).showIconPackDialog(); } } private void showHomeLauncherChooser() { if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).showHomeLauncherChooser(); } } private void showNightModeChooser() { if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).showNightModeChooser(); } } private void showBackgroundDialog() { if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).showBackgroundDialog(); } } private void showHighlightColorDialog() { if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).showHighlightColorDialog(); } } @Override public void onDefaultsReset() { resetToDefault(); assignValues(); } @Override public void onValuesUpdated() { assignValues(); } private void resetToDefault() { mSettings.save(Settings.KEY_VIBRATE_APP_HOVER, Settings.DEFAULT_VIBRATE_APP_HOVER); mSettings.save(Settings.KEY_VIBRATE_APP_LAUNCH, Settings.DEFAULT_VIBRATE_APP_LAUNCH); mSettings.save(Settings.KEY_SHOW_NAME_APP_HOVER, Settings.DEFAULT_SHOW_NAME_APP_HOVER); mSettings.save(Settings.KEY_SHOW_TOUCH_SELECTION, Settings.DEFAULT_SHOW_TOUCH_SELECTION); mSettings.save(Settings.KEY_SHOW_NEW_APP_TAG, Settings.DEFAULT_SHOW_NEW_APP_TAG); mSettings.save(Settings.KEY_BACKGROUND, Settings.DEFAULT_BACKGROUND); mSettings.save(Settings.KEY_BACKGROUND_COLOR, Settings.DEFAULT_BACKGROUND_COLOR); mSettings.save(Settings.KEY_HIGHLIGHT_COLOR, Settings.DEFAULT_HIGHLIGHT_COLOR); mSettings.save(Settings.KEY_ICON_PACK_LABEL_NAME, Settings.DEFAULT_ICON_PACK_LABEL_NAME); mSettings.save(Settings.KEY_NIGHT_MODE, Settings.DEFAULT_NIGHT_MODE); if (getActivity() != null && getActivity() instanceof SettingsActivity) { ((SettingsActivity) getActivity()).sendNightModeBroadcast(); } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/AppSorter.java ================================================ package nickrout.lenslauncher.util; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import nickrout.lenslauncher.R; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.model.AppPersistent; /** * Created by nicholasrout on 2016/05/28. */ public class AppSorter { public enum SortType { LABEL_ASCENDING(R.string.setting_sort_type_label_ascending), LABEL_DESCENDING(R.string.setting_sort_type_label_descending), INSTALL_DATE_ASCENDING(R.string.setting_sort_type_install_date_ascending), INSTALL_DATE_DESCENDING(R.string.setting_sort_type_install_date_descending), ICON_COLOR_ASCENDING(R.string.setting_sort_type_icon_color_ascending), ICON_COLOR_DESCENDING(R.string.setting_sort_type_icon_color_descending), OPEN_COUNT_ASCENDING(R.string.setting_sort_type_open_count_ascending), OPEN_COUNT_DESCENDING(R.string.setting_sort_type_open_count_descending); int mDisplayNameResId; SortType(int displayNameResId) { mDisplayNameResId = displayNameResId; } public int getDisplayNameResId() { return mDisplayNameResId; } } public static void sort(ArrayList apps, SortType sortType) { switch (sortType) { case LABEL_ASCENDING: sortByLabelAscending(apps); break; case LABEL_DESCENDING: sortByLabelDescending(apps); break; case INSTALL_DATE_ASCENDING: sortByInstallDateAscending(apps); break; case INSTALL_DATE_DESCENDING: sortByInstallDateDescending(apps); break; case OPEN_COUNT_ASCENDING: sortByOpenCountAscending(apps); break; case OPEN_COUNT_DESCENDING: sortByOpenCountDescending(apps); break; case ICON_COLOR_ASCENDING: sortByIconColorAscending(apps); break; case ICON_COLOR_DESCENDING: sortByIconColorDescending(apps); break; default: sortByLabelAscending(apps); break; } } private static void sortByLabelAscending(ArrayList apps) { Collections.sort(apps, new Comparator() { @Override public int compare(App a1, App a2) { return (a1.getLabel().toString()).compareToIgnoreCase(a2.getLabel().toString()); } }); } private static void sortByLabelDescending(ArrayList apps) { sortByLabelAscending(apps); Collections.reverse(apps); } private static void sortByInstallDateAscending(ArrayList apps) { Collections.sort(apps, new Comparator() { @Override public int compare(App a1, App a2) { if (a1.getInstallDate() > a2.getInstallDate()) { return -1; } else if (a1.getInstallDate() < a2.getInstallDate()) { return +1; } return 0; } }); } private static void sortByInstallDateDescending(ArrayList apps) { sortByInstallDateAscending(apps); Collections.reverse(apps); } private static void sortByOpenCountAscending(ArrayList apps) { Collections.sort(apps, new Comparator() { @Override public int compare(App a1, App a2) { long a1OpenCount = AppPersistent.getAppOpenCount(a1.getPackageName().toString(), a1.getName().toString()); long a2OpenCount = AppPersistent.getAppOpenCount(a2.getPackageName().toString(), a2.getName().toString()); if (a1OpenCount > a2OpenCount) { return -1; } else if (a1OpenCount < a2OpenCount) { return +1; } return 0; } }); } private static void sortByOpenCountDescending(ArrayList apps) { sortByOpenCountAscending(apps); Collections.reverse(apps); } private static void sortByIconColorAscending(ArrayList apps) { Collections.sort(apps, new Comparator() { @Override public int compare(App a1, App a2) { float a1HSVColor = ColorUtil.getHueColorFromApp(a1); float a2HSVColor = ColorUtil.getHueColorFromApp(a2); if (a1HSVColor > a2HSVColor) { return -1; } else if (a1HSVColor < a2HSVColor) { return +1; } return 0; } }); } private static void sortByIconColorDescending(ArrayList apps) { sortByIconColorAscending(apps); Collections.reverse(apps); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/AppUtil.java ================================================ package nickrout.lenslauncher.util; import android.app.Application; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.content.ContextCompat; import android.view.View; import android.widget.Toast; import java.util.ArrayList; import java.util.List; import nickrout.lenslauncher.R; import nickrout.lenslauncher.background.BroadcastReceivers; import nickrout.lenslauncher.model.App; import nickrout.lenslauncher.model.AppPersistent; /** * Created by nickrout on 2016/04/02. */ public class AppUtil { private static final String TAG = AppUtil.class.getSimpleName(); // Get all available apps for launcher public static ArrayList getApps( PackageManager packageManager, Context context, Application application, String iconPackLabelName, AppSorter.SortType sortType) { ArrayList apps = new ArrayList<>(); Intent intent = new Intent(Intent.ACTION_MAIN, null); intent.addCategory(Intent.CATEGORY_LAUNCHER); List availableActivities = null; try { availableActivities = packageManager.queryIntentActivities(intent, 0); } catch (RuntimeException e) { e.printStackTrace(); Toast.makeText(context, R.string.error_too_many_apps, Toast.LENGTH_SHORT).show(); } if (availableActivities != null) { IconPackManager.IconPack selectedIconPack = null; ArrayList iconPacks = new IconPackManager().getAvailableIconPacksWithIcons(true, application); for (IconPackManager.IconPack iconPack : iconPacks) { if (iconPack.mName.equals(iconPackLabelName)) selectedIconPack = iconPack; } for (int i = 0; i < availableActivities.size(); i++) { ResolveInfo resolveInfo = availableActivities.get(i); App app = new App(); app.setId(i); try { app.setInstallDate(packageManager.getPackageInfo(resolveInfo.activityInfo.packageName, 0).firstInstallTime); } catch (PackageManager.NameNotFoundException e) { app.setInstallDate(0); } app.setLabel(resolveInfo.loadLabel(packageManager)); app.setPackageName(resolveInfo.activityInfo.packageName); app.setName(resolveInfo.activityInfo.name); app.setIconResId(resolveInfo.activityInfo.getIconResource()); Bitmap defaultBitmap = BitmapUtil.packageNameToBitmap( context, packageManager, resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.getIconResource()); if (selectedIconPack != null) app.setIcon(selectedIconPack.getIconForPackage(app.getPackageName().toString(), defaultBitmap)); else app.setIcon(defaultBitmap); app.setPaletteColor(ColorUtil.getPaletteColorFromApp(app)); apps.add(app); } } AppSorter.sort(apps, sortType); return apps; } // Launch apps, for launcher :-P public static void launchComponent(Context context, String packageName, String name, View view, Rect bounds) { if (packageName != null && name != null) { Intent componentIntent = new Intent(); componentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); componentIntent.setComponent(new ComponentName(packageName, name)); if (!packageName.equals("nickrout.lenslauncher")) { componentIntent.setAction(Intent.ACTION_MAIN); } componentIntent.addCategory(Intent.CATEGORY_LAUNCHER); try { // Launch Component ContextCompat.startActivity(context, componentIntent, getLauncherOptionsBundle(context, view, bounds)); // Increment app open count AppPersistent.incrementAppCount(packageName, name); // Resort apps (if open count selected) Settings settings = new Settings(context); if (settings.getSortType() == AppSorter.SortType.OPEN_COUNT_ASCENDING || settings.getSortType() == AppSorter.SortType.OPEN_COUNT_DESCENDING) { Intent editAppsIntent = new Intent(context, BroadcastReceivers.AppsEditedReceiver.class); context.sendBroadcast(editAppsIntent); } } catch (ActivityNotFoundException e) { Toast.makeText(context, R.string.error_app_not_found, Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(context, R.string.error_app_not_found, Toast.LENGTH_SHORT).show(); } } private static Bundle getLauncherOptionsBundle(Context context, View source, Rect bounds) { Bundle optionsBundle = null; if (source != null) { ActivityOptionsCompat options; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && bounds != null) { // Clip reveal animation for Marshmallow and above options = ActivityOptionsCompat.makeClipRevealAnimation(source, bounds.left, bounds.top, bounds.width(), bounds.height()); } else { // Fade animation otherwise options = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.fade_in, R.anim.fade_out); } optionsBundle = options.toBundle(); } return optionsBundle; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/BitmapUtil.java ================================================ package nickrout.lenslauncher.util; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; /** * Created by nickrout on 2016/04/02. */ public class BitmapUtil { // Old method - does not allow for adding options i.e. lower quality // Source: http://stackoverflow.com/questions/3035692/how-to-convert-a-drawable-to-a-bitmap public static Bitmap drawableToBitmap (Drawable drawable) { Bitmap bitmap = null; if (drawable instanceof BitmapDrawable) { BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; bitmapDrawable.setAntiAlias(true); bitmapDrawable.setDither(true); bitmapDrawable.setTargetDensity(Integer.MAX_VALUE); if(bitmapDrawable.getBitmap() != null) { return bitmapDrawable.getBitmap(); } } if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) { bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel } else { bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); } Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); drawable.draw(canvas); return bitmap; } // Get bitmap from resource id, with quality options public static Bitmap resIdToBitmap(Resources res, int resId) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inScaled = false; options.inDither = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; options.inPreferQualityOverSpeed = true; try { return BitmapFactory.decodeResource(res, resId, options); } catch (OutOfMemoryError e1) { e1.printStackTrace(); return null; } } // Get res id from app package name public static int packageNameToResId(PackageManager packageManager, String packageName) { try { ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA); return applicationInfo.icon; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return -1; } } // Get bitmap from app package name public static Bitmap packageNameToBitmap(PackageManager packageManager, String packageName) { try { ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA); Resources resources = packageManager.getResourcesForApplication(applicationInfo); int appIconResId = applicationInfo.icon; return resIdToBitmap(resources, appIconResId); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); return null; } } // Get bitmap from app package name (with res id) public static Bitmap packageNameToBitmap(Context context, PackageManager packageManager, String packageName, int resId) { try { ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA); Resources resources = packageManager.getResourcesForApplication(applicationInfo); Bitmap bitmap = resIdToBitmap(resources, resId); if (bitmap == null) { Drawable drawable = packageManager.getApplicationIcon(packageName); if (drawable != null) { bitmap = drawableToBitmap(drawable); } } return bitmap; } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) { e.printStackTrace(); return null; } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/ColorUtil.java ================================================ package nickrout.lenslauncher.util; import android.graphics.Bitmap; import android.graphics.Color; import android.support.annotation.ColorInt; import android.support.v7.graphics.Palette; import nickrout.lenslauncher.model.App; /** * Created by nicholasrout on 2016/05/28. */ public class ColorUtil { public static @ColorInt int getPaletteColorFromApp(App app) { return getPaletteColorFromBitmap(app.getIcon()); } public static @ColorInt int getPaletteColorFromBitmap(Bitmap bitmap) { Palette palette; try { palette = Palette.from(bitmap).generate(); } catch (IllegalArgumentException e) { e.printStackTrace(); return Color.BLACK; } if (palette.getSwatches().size() > 0) { int swatchIndex = 0; for (int i = 1; i < palette.getSwatches().size(); i++) { if (palette.getSwatches().get(i).getPopulation() > palette.getSwatches().get(swatchIndex).getPopulation()) { swatchIndex = i; } } return palette.getSwatches().get(swatchIndex).getRgb(); } else { return Color.BLACK; } } public static float getHueColorFromApp(App app) { return getHueColorFromColor(app.getPaletteColor()); } public static float getHueColorFromColor(@ColorInt int color) { float[] hsvValues = new float[3]; Color.colorToHSV(color, hsvValues); return hsvValues[0]; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/IconPackManager.java ================================================ package nickrout.lenslauncher.util; /** * Created by rish on 22/5/16. */ import android.app.Application; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.support.annotation.ColorInt; import android.util.Log; 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.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import nickrout.lenslauncher.R; public class IconPackManager { private static final String TAG = "IconPackManager"; private Application mApplication; public class IconPack { public IconPack() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); mPaint.setFilterBitmap(true); mPaint.setDither(true); } public String mPackageName; public String mName; private boolean mLoaded = false; private HashMap mPackagesDrawables = new HashMap(); private List mBackImages = new ArrayList(); private Bitmap mMaskImage = null; private Bitmap mFrontImage = null; private float mFactor = 1.0f; private Paint mPaint; Resources mIconPackRes = null; public void load() { // Load appfilter.xml from the icon pack package PackageManager pm = mApplication.getPackageManager(); try { XmlPullParser xpp = null; mIconPackRes = pm.getResourcesForApplication(mPackageName); int appfilterid = mIconPackRes.getIdentifier("appfilter", "xml", mPackageName); if (appfilterid > 0) { xpp = mIconPackRes.getXml(appfilterid); } else { // No resource found, try to open it from assets folder try { InputStream appfilterstream = mIconPackRes.getAssets().open("appfilter.xml"); XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); xpp = factory.newPullParser(); xpp.setInput(appfilterstream, "utf-8"); } catch (IOException e1) { Log.d(TAG, "No appfilter.xml file"); } } if (xpp != null) { int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG) { if (xpp.getName().equals("iconback")) { for (int i = 0; i < xpp.getAttributeCount(); i++) { if (xpp.getAttributeName(i).startsWith("img")) { String drawableName = xpp.getAttributeValue(i); Bitmap iconback = loadBitmap(drawableName); if (iconback != null) mBackImages.add(iconback); } } } else if (xpp.getName().equals("iconmask")) { if (xpp.getAttributeCount() > 0 && xpp.getAttributeName(0).equals("img1")) { String drawableName = xpp.getAttributeValue(0); mMaskImage = loadBitmap(drawableName); } } else if (xpp.getName().equals("iconupon")) { if (xpp.getAttributeCount() > 0 && xpp.getAttributeName(0).equals("img1")) { String drawableName = xpp.getAttributeValue(0); mFrontImage = loadBitmap(drawableName); } } else if (xpp.getName().equals("scale")) { if (xpp.getAttributeCount() > 0 && xpp.getAttributeName(0).equals("factor")) { try { mFactor = Float.valueOf(xpp.getAttributeValue(0)); } catch (NumberFormatException e) { mFactor = 1.0f; e.printStackTrace(); } } } else if (xpp.getName().equals("item")) { String componentName = null; String drawableName = null; for (int i = 0; i < xpp.getAttributeCount(); i++) { if (xpp.getAttributeName(i).equals("component")) { componentName = xpp.getAttributeValue(i); } else if (xpp.getAttributeName(i).equals("drawable")) { drawableName = xpp.getAttributeValue(i); } } if (!mPackagesDrawables.containsKey(componentName)) mPackagesDrawables.put(componentName, drawableName); } } eventType = xpp.next(); } } mLoaded = true; } catch (PackageManager.NameNotFoundException e) { Log.d(TAG, "Cannot load icon pack"); } catch (XmlPullParserException e) { Log.d(TAG, "Cannot parse icon pack appfilter.xml"); } catch (IOException e) { e.printStackTrace(); } } private Bitmap loadBitmap(String drawableName) { int id = mIconPackRes.getIdentifier(drawableName, "drawable", mPackageName); if (id > 0) { Drawable bitmap = mIconPackRes.getDrawable(id); if (bitmap instanceof BitmapDrawable) return ((BitmapDrawable) bitmap).getBitmap(); } return null; } public Bitmap getIconForPackage(String appPackageName, Bitmap defaultBitmap) { if (!mLoaded) { load(); } PackageManager pm = mApplication.getPackageManager(); Intent launchIntent = pm.getLaunchIntentForPackage(appPackageName); String componentName = null; if (launchIntent != null) componentName = pm.getLaunchIntentForPackage(appPackageName).getComponent().toString(); String drawable = mPackagesDrawables.get(componentName); if (drawable != null) { Bitmap bitmap = loadBitmap(drawable); if (bitmap == null) { return generateBitmap(defaultBitmap); } return bitmap; } else { // Try to get a resource with the component filename if (componentName != null) { int start = componentName.indexOf("{") + 1; int end = componentName.indexOf("}", start); if (end > start) { drawable = componentName.substring(start, end).toLowerCase(Locale.getDefault()).replace(".", "_").replace("/", "_"); if (mIconPackRes.getIdentifier(drawable, "drawable", mPackageName) > 0) return loadBitmap(drawable); } } } return generateBitmap(defaultBitmap); } private Bitmap generateBitmap(Bitmap defaultBitmap) { // No need to go through below process id defaultBitmap is null if (defaultBitmap == null) { return null; } // If no back images, return default app icon if (mBackImages.size() == 0) { return defaultBitmap; } // Get a random back image Bitmap backImage = getMostAppropriateBackImage(defaultBitmap); int backImageWidth = backImage.getWidth(); int backImageHeight = backImage.getHeight(); // Create a bitmap for the result Bitmap result; try { result = Bitmap.createBitmap(backImageWidth, backImageHeight, Bitmap.Config.ARGB_8888); } catch (OutOfMemoryError e) { e.printStackTrace(); return null; } // Instantiate a canvas to combine the icon / background Canvas canvas = new Canvas(result); // Draw the background first canvas.drawBitmap(backImage, 0, 0, null); // Create rects for scaling the default bitmap Rect srcRect = new Rect( 0, 0, defaultBitmap.getWidth(), defaultBitmap.getHeight() ); float scaledWidth = mFactor * ((float) backImageWidth); float scaledHeight = mFactor * ((float) backImageHeight); RectF destRect = new RectF( ((float) backImageWidth) / 2.0f - scaledWidth / 2.0f, ((float) backImageHeight) / 2.0f - scaledHeight / 2.0f, ((float) backImageWidth) / 2.0f + scaledWidth / 2.0f, ((float) backImageHeight) / 2.0f + scaledHeight / 2.0f ); // Handle mask image if (mMaskImage != null) { // First get mask bitmap Bitmap mask; try { mask = Bitmap.createBitmap(backImageWidth, backImageHeight, Bitmap.Config.ARGB_8888); } catch (OutOfMemoryError e) { e.printStackTrace(); return null; } // Make a temp mask canvas Canvas maskCanvas = new Canvas(mask); // Draw the bitmap with mask into the result maskCanvas.drawBitmap( defaultBitmap, srcRect, destRect, mPaint ); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); maskCanvas.drawBitmap(mMaskImage, 0, 0, mPaint); mPaint.setXfermode(null); canvas.drawBitmap(mask, 0, 0, mPaint); } else { // Draw the scaled bitmap without mask canvas.drawBitmap( defaultBitmap, srcRect, destRect, mPaint ); } // Draw the front image if (mFrontImage != null) { canvas.drawBitmap(mFrontImage, 0, 0, mPaint); } // Return result return result; } private Bitmap getMostAppropriateBackImage(Bitmap defaultBitmap) { if (mBackImages.size() == 1) { return mBackImages.get(0); } @ColorInt int defaultPaletteColor = ColorUtil.getPaletteColorFromBitmap(defaultBitmap); float defaultHueColor = ColorUtil.getHueColorFromColor(defaultPaletteColor); float difference = Float.MAX_VALUE; int index = 0; for (int i = 0; i < mBackImages.size(); i++) { @ColorInt int backPaletteColor = ColorUtil.getPaletteColorFromBitmap(mBackImages.get(i)); float backHueColor = ColorUtil.getHueColorFromColor(backPaletteColor); if (Math.abs(defaultHueColor - backHueColor) < difference) { difference = Math.abs(defaultHueColor - backHueColor); index = i; } } return mBackImages.get(index); } } private ArrayList mIconPacks = null; public ArrayList getAvailableIconPacksWithIcons(boolean forceReload, Application application) { mApplication = application; if (mIconPacks == null || forceReload) { mIconPacks = new ArrayList<>(); PackageManager pm = mApplication.getPackageManager(); /** * Currently Lens Launcher supports all icon packs supported by GoLauncher / EX * Create a set by filter the list to contain unique icon packs only */ List rinfo = new ArrayList<>(); for (String launcher : mApplication.getResources().getStringArray(R.array.icon_pack_launchers)) { rinfo.addAll(pm.queryIntentActivities(new Intent(launcher), PackageManager.GET_META_DATA)); } for (ResolveInfo ri : rinfo) { IconPack ip = new IconPack(); ip.mPackageName = ri.activityInfo.packageName; ApplicationInfo ai = null; try { ai = pm.getApplicationInfo(ip.mPackageName, PackageManager.GET_META_DATA); ip.mName = mApplication.getPackageManager().getApplicationLabel(ai).toString(); mIconPacks.add(ip); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } } return mIconPacks; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/LauncherUtil.java ================================================ package nickrout.lenslauncher.util; import android.app.Application; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; /** * Created by rish on 5/6/16. */ public class LauncherUtil { public static boolean isLauncherDefault(Application application) { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); final ResolveInfo res = application.getPackageManager().resolveActivity(intent, 0); if (res.activityInfo == null) { return false; } else if ("android".equals(res.activityInfo.packageName)) { return false; } else { if (res.activityInfo.packageName.equals(application.getPackageName())) return true; else return false; } } public static String getHomeLauncherName(Application application) { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); final ResolveInfo res = application.getPackageManager().resolveActivity(intent, 0); if (res.activityInfo == null) { return ""; } return res.activityInfo.loadLabel(application.getPackageManager()).toString(); } public static void resetPreferredLauncherAndOpenChooser(Context context) { PackageManager packageManager = context.getPackageManager(); ComponentName componentName = new ComponentName(context, nickrout.lenslauncher.ui.FakeLauncherActivity.class); packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); Intent selector = new Intent(Intent.ACTION_MAIN); selector.addCategory(Intent.CATEGORY_HOME); selector.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(selector); packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT, PackageManager.DONT_KILL_APP); } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/LensCalculator.java ================================================ package nickrout.lenslauncher.util; import android.content.Context; import android.content.res.Resources; import android.graphics.RectF; import android.util.DisplayMetrics; import nickrout.lenslauncher.model.Grid; /** * Created by nickrout on 2016/04/02. */ public class LensCalculator { // Algorithm for calculating equispaced grid public static Grid calculateGrid(Context context, int screenWidth, int screenHeight, int itemCount) { Grid grid = new Grid(); grid.setItemCount(itemCount); int itemCountHorizontal, itemCountVertical; if (itemCount == 0 || itemCount == 1) { itemCountHorizontal = itemCount; itemCountVertical = itemCount; } else { double optimalSquareSize = calculateOptimalSquareSize(screenWidth, screenHeight, itemCount); itemCountHorizontal = (int) Math.ceil(screenWidth / optimalSquareSize); itemCountVertical = (int) Math.ceil((double) itemCount / (double) itemCountHorizontal); } grid.setItemCountHorizontal(itemCountHorizontal); grid.setItemCountVertical(itemCountVertical); Settings settings = new Settings(context); float itemSize = LensCalculator.convertDpToPixel(settings.getFloat(Settings.KEY_ICON_SIZE), context); grid.setItemSize(itemSize); float spacingHorizontal = (((float) screenWidth) - ((float) itemCountHorizontal * itemSize)) / ((float) (itemCountHorizontal + 1)); grid.setSpacingHorizontal(spacingHorizontal); float spacingVertical = (((float) screenHeight) - ((float) itemCountVertical * itemSize)) / ((float) (itemCountVertical + 1)); grid.setSpacingVertical(spacingVertical); return grid; } // Algorithm for calculating optimal square side length given width, height and number of items public static double calculateOptimalSquareSize(int screenWidth, int screenHeight, int itemCount) { // Source: http://math.stackexchange.com/questions/466198/algorithm-to-get-the-maximum-size-of-n-squares-that-fit-into-a-rectangle-with-a double x = (double) screenWidth, y = (double) screenHeight, n = (double) itemCount; double px = Math.ceil(Math.sqrt(n * x / y)); double sx, sy; if(Math.floor(px * y / x) * px < n) { sx = y / Math.ceil(px * y / x); } else { sx = x / px; } double py = Math.ceil(Math.sqrt(n * y / x)); if(Math.floor(py * x / y) * py < n) { sy = x / Math.ceil(x * py / y); } else { sy = y / py; } return Math.max(sx, sy); } // Algorithm for circular distance public static double calculateDistance(float x1, float x2, float y1, float y2) { return Math.sqrt(Math.pow((double) (x2 - x1), 2) + Math.pow((double) (y2 - y1), 2)); } // Algorithm for determining whether a rect is within a given lens (centered at touchX, touchY) public static boolean isRectWithinLens(RectF rect, float touchX, float touchY, float lensDiameter) { if (rect.left >= touchX - lensDiameter / 2.0f && rect.right <= touchX + lensDiameter / 2.0f && rect.top >= touchY - lensDiameter / 2.0f && rect.bottom <= touchY + lensDiameter / 2.0f) { return true; } else { return false; } } // Graphical Fisheye Lens algorithm for shifting public static float shiftPoint(Context context, float lensPosition, float itemPosition, float boundary, float multiplier) { if (lensPosition < 0) { return itemPosition; } Settings settings = new Settings(context); float shiftedPosition; float a = Math.abs(lensPosition - itemPosition); float b = Math.max(lensPosition, boundary - lensPosition); float x = a / b; float d = multiplier * settings.getFloat(Settings.KEY_DISTORTION_FACTOR); float y = ((1.0f + d) * x) / (1.0f + (d * x)); float newDistanceFromCenter = b * y; if (lensPosition >= itemPosition) { shiftedPosition = lensPosition - newDistanceFromCenter; } else { shiftedPosition = lensPosition + newDistanceFromCenter; } return shiftedPosition; } // Graphical Fisheye Lens algorithm for scaling public static float scalePoint(Context context, float lensPosition, float itemPosition, float itemSize, float boundary, float multiplier) { if (lensPosition < 0) { return itemSize; } Settings settings = new Settings(context); float scaleDifference = settings.getFloat(Settings.KEY_SCALE_FACTOR) - Settings.MIN_SCALE_FACTOR; float d = Settings.MIN_SCALE_FACTOR + scaleDifference * multiplier; if (lensPosition >= itemPosition) { itemPosition = itemPosition - d * (itemSize / 2.0f); } else { itemPosition = itemPosition + d * (itemSize / 2.0f); } return LensCalculator.shiftPoint(context, lensPosition, itemPosition, boundary, multiplier); } // Graphical Fisheye Lens algorithm for determining final scaled size public static float calculateSquareScaledSize(float scaledPositionX, float shiftedPositionX, float scaledPositionY, float shiftedPositionY) { return 2.0f * Math.min(Math.abs(scaledPositionX - shiftedPositionX), Math.abs(scaledPositionY - shiftedPositionY)); } // Algorithm for calculating new rect public static RectF calculateRect(float newCenterX, float newCenterY, float newSize) { RectF newRect = new RectF( newCenterX - newSize / 2.0f, newCenterY - newSize / 2.0f, newCenterX + newSize / 2.0f, newCenterY + newSize / 2.0f); return newRect; } // Algorithm for determining if touch point is within rect public static boolean isInsideRect(float x, float y, RectF rect) { if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) { return true; } else { return false; } } // Algorithm for converting dp measurements to pixels public static float convertDpToPixel(float dp, Context context) { Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); float px = dp * ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); return px; } // Algorithm for converting pixels to dp measurements public static float convertPixelsToDp(float px, Context context) { Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); float dp = px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); return dp; } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/NightModeUtil.java ================================================ package nickrout.lenslauncher.util; import android.support.v7.app.AppCompatDelegate; /** * Created by nicholasrout on 2017/01/15. */ public class NightModeUtil { public static String getNightModeDisplayName(@AppCompatDelegate.NightMode int nightMode) { switch (nightMode) { case AppCompatDelegate.MODE_NIGHT_NO: return "Light"; case AppCompatDelegate.MODE_NIGHT_YES: return "Dark"; case AppCompatDelegate.MODE_NIGHT_AUTO: return "Auto"; default: return "Follow System"; } } public static @AppCompatDelegate.NightMode int getNightModeFromDisplayName(String displayName) { switch (displayName) { case "Light": return AppCompatDelegate.MODE_NIGHT_NO; case "Dark": return AppCompatDelegate.MODE_NIGHT_YES; case "Auto": return AppCompatDelegate.MODE_NIGHT_AUTO; default: return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM; } } } ================================================ FILE: app/src/main/java/nickrout/lenslauncher/util/Settings.java ================================================ package nickrout.lenslauncher.util; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; import android.support.v7.app.AppCompatDelegate; /** * Created by nicholasrout on 2016/04/02. */ public class Settings { public static final float DEFAULT_ICON_SIZE = 18.0f; public static final float DEFAULT_DISTORTION_FACTOR = 2.5f; public static final float DEFAULT_SCALE_FACTOR = 1.0f; public static final long DEFAULT_ANIMATION_TIME = 200; public static final boolean DEFAULT_VIBRATE_APP_HOVER = false; public static final boolean DEFAULT_VIBRATE_APP_LAUNCH = true; public static final boolean DEFAULT_SHOW_NAME_APP_HOVER = true; public static final boolean DEFAULT_SHOW_TOUCH_SELECTION = false; public static final boolean DEFAULT_SHOW_NEW_APP_TAG = true; public static final String DEFAULT_BACKGROUND = "Wallpaper"; public static final String DEFAULT_BACKGROUND_COLOR = "#FFF8BBD0"; public static final String DEFAULT_HIGHLIGHT_COLOR = "#FFF50057"; public static final String DEFAULT_ICON_PACK_LABEL_NAME = "Default Icon Pack"; public static final int DEFAULT_SORT_TYPE = 0; public static final int DEFAULT_NIGHT_MODE = AppCompatDelegate.MODE_NIGHT_NO; // These values are for the progress bars, their real values = (MAX_VALUE / INTERVAL (eg. 2)) + MIN_VALUE public static final int MAX_ICON_SIZE = 45; public static final int MAX_DISTORTION_FACTOR = 9; public static final int MAX_SCALE_FACTOR = 5; public static final int MAX_ANIMATION_TIME = 600; public static final int SHOW_NEW_APP_TAG_DURATION = 12 * 60 * 60 * 1000; /* An app has the new tag for twelve hours. If openCount >= 1, the new tag is not drawn. */ public static final float MIN_ICON_SIZE = 10.0f; public static final float MIN_DISTORTION_FACTOR = 0.5f; public static final float MIN_SCALE_FACTOR = 1.0f; public static final long MIN_ANIMATION_TIME = 100; public static final float DEFAULT_FLOAT = Float.MIN_VALUE; public static final long DEFAULT_LONG = Long.MIN_VALUE; public static final boolean DEFAULT_BOOLEAN = false; public static final String DEFAULT_STRING = ""; public static final String KEY_ICON_SIZE = "min_icon_size"; public static final String KEY_DISTORTION_FACTOR = "distortion_factor"; public static final String KEY_SCALE_FACTOR = "scale_factor"; public static final String KEY_ANIMATION_TIME = "animation_time"; public static final String KEY_VIBRATE_APP_HOVER = "vibrate_app_hover"; public static final String KEY_VIBRATE_APP_LAUNCH = "vibrate_app_launch"; public static final String KEY_SHOW_NAME_APP_HOVER = "show_name_app_hover"; public static final String KEY_SHOW_TOUCH_SELECTION = "show_touch_selection"; public static final String KEY_SHOW_NEW_APP_TAG = "show_new_tag_app"; public static final String KEY_BACKGROUND = "background"; public static final String KEY_BACKGROUND_COLOR = "background_color"; public static final String KEY_HIGHLIGHT_COLOR = "show_touch_selection_color"; public static final String KEY_ICON_PACK_LABEL_NAME = "icon_pack_label_name"; public static final String KEY_SORT_TYPE = "sort_type"; public static final String KEY_NIGHT_MODE = "night_mode"; private Context mContext; private SharedPreferences mPrefs; public Settings(Context context) { mContext = context; } private SharedPreferences sharedPreferences() { if (mPrefs == null) { mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); } return mPrefs; } public void save(String name, int value) { sharedPreferences().edit().putInt(name, value).commit(); } public @AppCompatDelegate.NightMode int getNightMode() { switch (sharedPreferences().getInt(KEY_NIGHT_MODE, DEFAULT_NIGHT_MODE)) { case AppCompatDelegate.MODE_NIGHT_AUTO: return AppCompatDelegate.MODE_NIGHT_AUTO; case AppCompatDelegate.MODE_NIGHT_NO: return AppCompatDelegate.MODE_NIGHT_NO; case AppCompatDelegate.MODE_NIGHT_YES: return AppCompatDelegate.MODE_NIGHT_YES; default: return AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM; } } public void save(String name, float value) { sharedPreferences().edit().putFloat(name, value).commit(); } public float getFloat(String name) { if (name.equals(KEY_ICON_SIZE)) { if (sharedPreferences().getFloat(name, DEFAULT_ICON_SIZE) < MIN_ICON_SIZE) { save(name, MIN_ICON_SIZE); } else if (sharedPreferences().getFloat(name, DEFAULT_ICON_SIZE) > getMaxFloatValue(name)) { save(name, getMaxFloatValue(name)); } return sharedPreferences().getFloat(name, DEFAULT_ICON_SIZE); } else if (name.equals(KEY_DISTORTION_FACTOR)) { if (sharedPreferences().getFloat(name, DEFAULT_DISTORTION_FACTOR) < MIN_DISTORTION_FACTOR) { save(name, MIN_DISTORTION_FACTOR); } else if (sharedPreferences().getFloat(name, DEFAULT_DISTORTION_FACTOR) > getMaxFloatValue(name)) { save(name, getMaxFloatValue(name)); } return sharedPreferences().getFloat(name, DEFAULT_DISTORTION_FACTOR); } else if (name.equals(KEY_SCALE_FACTOR)) { if (sharedPreferences().getFloat(name, DEFAULT_SCALE_FACTOR) < MIN_SCALE_FACTOR) { save(name, MIN_SCALE_FACTOR); } else if (sharedPreferences().getFloat(name, DEFAULT_SCALE_FACTOR) > getMaxFloatValue(name)) { save(name, getMaxFloatValue(name)); } return sharedPreferences().getFloat(name, DEFAULT_SCALE_FACTOR); } else { return sharedPreferences().getFloat(name, DEFAULT_FLOAT); } } public void save(String name, long value) { sharedPreferences().edit().putLong(name, value).commit(); } public long getLong(String name) { if (name.equals(KEY_ANIMATION_TIME)) { if (sharedPreferences().getLong(name, DEFAULT_ANIMATION_TIME) < MIN_ANIMATION_TIME) { save(name, MIN_ANIMATION_TIME); } else if (sharedPreferences().getLong(name, DEFAULT_ANIMATION_TIME) > getMaxLongValue(name)) { save(name, getMaxLongValue(name)); } return sharedPreferences().getLong(name, DEFAULT_ANIMATION_TIME); } else { return sharedPreferences().getLong(name, DEFAULT_LONG); } } public void save(String name, String value) { sharedPreferences().edit().putString(name, value).commit(); } public String getString(String name) { if (name.equals(KEY_BACKGROUND)) { return sharedPreferences().getString(name, DEFAULT_BACKGROUND); } else if (name.equals(KEY_BACKGROUND_COLOR)) { return sharedPreferences().getString(name, DEFAULT_BACKGROUND_COLOR); } else if (name.equals(KEY_HIGHLIGHT_COLOR)) { return sharedPreferences().getString(name, DEFAULT_HIGHLIGHT_COLOR); } else if (name.equals(KEY_ICON_PACK_LABEL_NAME)) { return sharedPreferences().getString(name, DEFAULT_ICON_PACK_LABEL_NAME); } else { return sharedPreferences().getString(name, DEFAULT_STRING); } } public void save(String name, boolean value) { sharedPreferences().edit().putBoolean(name, value).commit(); } public boolean getBoolean(String name) { if (name.equals(KEY_VIBRATE_APP_HOVER)) { return sharedPreferences().getBoolean(name, DEFAULT_VIBRATE_APP_HOVER); } else if (name.equals(KEY_VIBRATE_APP_LAUNCH)) { return sharedPreferences().getBoolean(name, DEFAULT_VIBRATE_APP_LAUNCH); } else if (name.equals(KEY_SHOW_NAME_APP_HOVER)) { return sharedPreferences().getBoolean(name, DEFAULT_SHOW_NAME_APP_HOVER); } else if (name.equals(KEY_SHOW_TOUCH_SELECTION)) { return sharedPreferences().getBoolean(name, DEFAULT_SHOW_TOUCH_SELECTION); } else if (name.equals(KEY_SHOW_NEW_APP_TAG)) { return sharedPreferences().getBoolean(name, DEFAULT_SHOW_NEW_APP_TAG); } else { return sharedPreferences().getBoolean(name, DEFAULT_BOOLEAN); } } public void save(AppSorter.SortType value) { save(KEY_SORT_TYPE, value.ordinal()); } public AppSorter.SortType getSortType() { int ordinal = sharedPreferences().getInt(KEY_SORT_TYPE, DEFAULT_SORT_TYPE); return AppSorter.SortType.values()[ordinal]; } public float getMaxFloatValue(String name) { if (name.equals(KEY_ICON_SIZE)) { return (float) MAX_ICON_SIZE + MIN_ICON_SIZE; } else if (name.equals(KEY_DISTORTION_FACTOR)) { return (float) MAX_DISTORTION_FACTOR / 2 + MIN_DISTORTION_FACTOR; } else if (name.equals(KEY_SCALE_FACTOR)) { return (float) MAX_SCALE_FACTOR / 2 + MIN_SCALE_FACTOR; } else { return DEFAULT_FLOAT; } } public long getMaxLongValue(String name) { if (name.equals(KEY_ANIMATION_TIME)) { return (long) MAX_ANIMATION_TIME / 2 + MIN_ANIMATION_TIME; } else { return DEFAULT_LONG; } } } ================================================ FILE: app/src/main/res/anim/fade_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/fade_out.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_in_left.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_in_right.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_out_left.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_out_right.xml ================================================ ================================================ FILE: app/src/main/res/animator/raise.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_about.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_fake_launcher.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_home.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_settings.xml ================================================ ================================================ FILE: app/src/main/res/layout/fragment_apps.xml ================================================ ================================================ FILE: app/src/main/res/layout/fragment_lens.xml ================================================ ================================================ FILE: app/src/main/res/layout/fragment_settings.xml ================================================ ================================================ FILE: app/src/main/res/layout/layout_pro.xml ================================================ ================================================ FILE: app/src/main/res/layout/recycler_item_app.xml ================================================ ================================================ FILE: app/src/main/res/menu/menu_app.xml ================================================ ================================================ FILE: app/src/main/res/menu/menu_settings.xml ================================================ ================================================ FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml ================================================ ================================================ FILE: app/src/main/res/values/arrays.xml ================================================ com.gau.go.launcherex.theme org.adw.launcher.THEMES Wallpaper Color Light Dark Auto Follow System ================================================ FILE: app/src/main/res/values/colors.xml ================================================ #111111 #000000 #F50057 #000000 #FFFFFF #00FFFFFF #DDDDDD @color/colorBlack #80000000 @color/colorBlack @color/colorBlack ================================================ FILE: app/src/main/res/values/dimen.xml ================================================ ]> 50dp 3dp 14sp 1dp 8dp 1dp 80dp 0dp 3dp 6dp 360dp 32dp 16dp 32sp 32dp 16dp 48dp 64dp 4dp 8dp 16dp 16dp 8dp 16dp ================================================ FILE: app/src/main/res/values/integers.xml ================================================ 1 ================================================ FILE: app/src/main/res/values/strings.xml ================================================ Lens Launcher About Order & Visibility Settings Apps Lens Apps Settings Lens Diameter Icon Size Distortion Factor Scale Factor Animation Time Vibrate on App Hover Vibrate on App Launch Show Name on App Hover Show Touch Selection Show New App Tag Background Color Highlight Color Icon Pack Home Launcher Night Mode Theme Background Default Icon Pack @string/setting_sort_type_label_ascending App Name (Ascending) App Name (Descending) Newest First Oldest First Most Opened Least Opened Icon Color (Light to Dark) Icon Color (Dark to Light) @string/activity_title_arranger App Order About Reset Default Settings Change Home Launcher Show Apps Open App Info Uninstall App Not Found Failed to Load Apps Loading Apps Loading Icons Values Reset To Default
Instead of scrolling long lists or swiping through multiple pages, Lens Launcher implements two unique features:

• An Equispaced Grid that displays all of your apps, regardless of screen size or app count.

• A Graphical Fisheye Lens to quickly zoom, pan and launch apps using touch gestures.


Lens Launcher also includes a full Settings menu to tweak all aspects of the Lens; Distortion, Scaling, Icon Size, Haptic Feedback, etc.

The Graphical Fisheye Lens algorithm is derived from methods proposed by Manojit Sarkar and Marc H. Brown. Their original 1993 paper, titled Graphical Fisheye Views, can be found here.

Lens Launcher is written by Nick Rout
Twitter | Google+

Major contributions have been made by Rish Bhardwaj
Website | LinkedIn

The source code and resources are available on Github

Check out ShortStories by Nick Rout. Interactive stories told through App Shortcuts and Notifications.
Get it on Google Play]]>
MainActivity PRO Get Lens Launcher PRO
================================================ FILE: app/src/main/res/values/styles.xml ================================================