├── .idea ├── .name ├── vcs.xml ├── copyright │ ├── profiles_settings.xml │ └── LauncherTV.xml ├── encodings.xml ├── .gitignore └── codeStyleSettings.xml ├── launcher ├── .gitignore ├── src │ └── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ ├── values-w720dp │ │ │ ├── strings.xml │ │ │ └── dimens.xml │ │ ├── values-w1080dp │ │ │ ├── dimens.xml │ │ │ └── strings.xml │ │ ├── values │ │ │ ├── attrs.xml │ │ │ ├── dimens.xml │ │ │ ├── styles.xml │ │ │ ├── notrans.xml │ │ │ └── strings.xml │ │ ├── layout │ │ │ ├── activity_launcher.xml │ │ │ ├── slider_preference_dialog.xml │ │ │ ├── grid_item.xml │ │ │ ├── list_item.xml │ │ │ ├── application.xml │ │ │ ├── gridview.xml │ │ │ ├── listview.xml │ │ │ └── fragment_application.xml │ │ ├── drawable │ │ │ ├── ic_add.xml │ │ │ ├── ic_delete.xml │ │ │ ├── application_focused.xml │ │ │ ├── application_normal.xml │ │ │ ├── application_pressed.xml │ │ │ ├── ic_close.xml │ │ │ ├── ic_tune.xml │ │ │ ├── ic_apps.xml │ │ │ └── application_selector.xml │ │ ├── values-zh-rCN │ │ │ └── strings.xml │ │ ├── values-cs │ │ │ └── strings.xml │ │ ├── values-fr │ │ │ └── strings.xml │ │ ├── values-sk │ │ │ └── strings.xml │ │ └── xml │ │ │ └── preferences.xml │ │ ├── java │ │ └── org │ │ │ └── cosinus │ │ │ └── launchertv │ │ │ ├── AppInfo.java │ │ │ ├── views │ │ │ ├── ApplicationAdapter.java │ │ │ ├── SliderPreference.java │ │ │ └── ApplicationView.java │ │ │ ├── Launcher.java │ │ │ ├── Utils.java │ │ │ ├── Setup.java │ │ │ ├── activities │ │ │ ├── ApplicationList.java │ │ │ └── Preferences.java │ │ │ └── fragments │ │ │ └── ApplicationFragment.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro ├── lint.xml └── build.gradle ├── settings.gradle ├── .linthub.yml ├── launchertv.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── NOTICE ├── .travis.yml ├── .gitignore ├── README.md ├── gradle.properties ├── style ├── suppressions.xml └── checkstyle.xml ├── LauncherTV.iml ├── gradlew.bat ├── gradlew ├── LauncherTV (Code Style).xml └── LICENSE /.idea/.name: -------------------------------------------------------------------------------- 1 | LauncherTV -------------------------------------------------------------------------------- /launcher/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':launcher' 2 | -------------------------------------------------------------------------------- /.linthub.yml: -------------------------------------------------------------------------------- 1 | java: 2 | lint: true 3 | config: style/checkstyle.xml 4 | -------------------------------------------------------------------------------- /launchertv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launchertv.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /launcher/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launcher/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /launcher/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launcher/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /launcher/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launcher/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /launcher/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launcher/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /launcher/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launcher/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /launcher/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alescdb/LauncherTV/HEAD/launcher/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Apache Simple TV Launcher 2 | Copyright 2015-2017 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | 7 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 16 13:05:28 CET 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip 7 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | *.xml 2 | 3 | !/codeStyleSettings.xml 4 | !/copyright/*.xml 5 | !/fileColors.xml 6 | !/encodings.xml 7 | !/runConfigurations/*.xml 8 | 9 | !/inspectionProfiles/*.xml 10 | /inspectionProfiles/profiles_settings.xml 11 | 12 | !/scopes/*.xml 13 | /scopes/scope_settings.xml 14 | 15 | !/templateLanguages.xml 16 | !/vcs.xml 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | sudo: true # For the 7.5GB limit 4 | 5 | notifications: 6 | email: 7 | - github@cosinus.org 8 | 9 | android: 10 | components: 11 | - tools 12 | - platform-tools 13 | - build-tools-26.0.2 14 | - android-25 15 | - extra-google-m2repository 16 | - extra-android-m2repository 17 | 18 | license: 19 | - 'android-sdk-license-.+' 20 | - 'google-gdk-license-.+' 21 | 22 | before_script: 23 | - chmod 0755 ./gradlew 24 | 25 | script: 26 | - ./gradlew assembleDebug 27 | 28 | after_failure: "cat $TRAVIS_BUILD_DIR/app/build/outputs/lint-results-debug.xml" 29 | after_success: ./gradlew coveralls 30 | -------------------------------------------------------------------------------- /launcher/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Utils\Android Studio\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -dontwarn 12 | 13 | # If your project uses WebView with JS, uncomment the following 14 | # and specify the fully qualified class name to the JavaScript interface 15 | # class: 16 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 17 | # public *; 18 | #} 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | launcher/gradle.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # Intellij 37 | *.iml 38 | !LauncherTV.iml 39 | .idea/workspace.xml 40 | 41 | # Keystore files 42 | *.jks 43 | *.keystore 44 | 45 | # Mac 46 | .DS_Store 47 | 48 | # Build scripts 49 | beforebuild.sh 50 | build.sh 51 | -------------------------------------------------------------------------------- /launcher/lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/copyright/LauncherTV.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Status 2 | ---------------- 3 | 4 | [![Build Status](https://travis-ci.org/alescdb/LauncherTV.svg?branch=master)](https://travis-ci.org/alescdb/LauncherTV) 5 | [![Coverage Status](https://coveralls.io/repos/github/alescdb/LauncherTV/badge.svg)](https://coveralls.io/github/alescdb/LauncherTV) 6 | 7 | Google Play 8 | ---------------- 9 | 10 | [![Get it on Google Play][2]][1] 11 | 12 | [1]: https://play.google.com/store/apps/details?id=org.cosinus.launchertv 13 | [2]: https://developer.android.com/images/brand/en_generic_rgb_wo_60.png 14 | 15 | 16 | Compiling 17 | ---------------- 18 | 19 | To compile Simple TV Launcher using gradlew, you must first specify where 20 | your Android SDK is via the local.properties file. Insert a line 21 | similar to the following with the full path to your SDK: 22 | 23 | ``` 24 | sdk.dir=/opt/android-sdk 25 | ``` 26 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-w720dp/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | Lock icons 21 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-w1080dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 64dp 20 | 128dp 21 | 22 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-w720dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 64dp 20 | 96dp 21 | 22 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-w1080dp/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | Prevent icons to be accidentally removed 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | -------------------------------------------------------------------------------- /launcher/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /style/suppressions.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /launcher/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 16dp 20 | 16dp 21 | 22 | 23 | 32dp 24 | 25 | 26 | -------------------------------------------------------------------------------- /LauncherTV.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/activity_launcher.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/application_focused.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 23 | 25 | 30 | 32 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/application_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 23 | 25 | 30 | 32 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/application_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 23 | 25 | 30 | 32 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/ic_tune.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/ic_apps.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /launcher/src/main/res/drawable/application_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/slider_preference_dialog.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 27 | 28 | 32 | 33 | 39 | 40 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/grid_item.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 26 | 27 | 33 | 34 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/list_item.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 26 | 27 | 35 | 36 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /style/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/AppInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv; 19 | 20 | import android.content.pm.ApplicationInfo; 21 | import android.content.pm.PackageManager; 22 | import android.content.pm.ResolveInfo; 23 | import android.graphics.drawable.Drawable; 24 | import android.support.annotation.NonNull; 25 | 26 | 27 | public class AppInfo { 28 | private final Drawable mIcon; 29 | private String mName; 30 | private final String mPackageName; 31 | 32 | AppInfo(PackageManager packageManager, ResolveInfo resolveInfo) { 33 | mPackageName = resolveInfo.activityInfo.packageName; 34 | mIcon = resolveInfo.loadIcon(packageManager); 35 | try { 36 | mName = resolveInfo.loadLabel(packageManager).toString(); 37 | } catch (Exception e) { 38 | mName = mPackageName; 39 | } 40 | } 41 | 42 | public AppInfo(PackageManager packageManager, ApplicationInfo applicationInfo) { 43 | mPackageName = applicationInfo.packageName; 44 | mIcon = applicationInfo.loadIcon(packageManager); 45 | try { 46 | mName = applicationInfo.loadLabel(packageManager).toString(); 47 | } catch (Exception e) { 48 | mName = mPackageName; 49 | } 50 | } 51 | 52 | 53 | @NonNull 54 | public String getName() { 55 | if (mName != null) 56 | return mName; 57 | return (""); 58 | } 59 | 60 | public Drawable getIcon() { 61 | return mIcon; 62 | } 63 | 64 | public String getPackageName() { 65 | return mPackageName; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/views/ApplicationAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv.views; 19 | 20 | import android.content.Context; 21 | import android.support.annotation.NonNull; 22 | import android.view.View; 23 | import android.view.ViewGroup; 24 | import android.widget.ArrayAdapter; 25 | import android.widget.ImageView; 26 | import android.widget.TextView; 27 | 28 | import org.cosinus.launchertv.AppInfo; 29 | import org.cosinus.launchertv.R; 30 | 31 | public class ApplicationAdapter extends ArrayAdapter { 32 | private final int mResource; 33 | 34 | public ApplicationAdapter(Context context, int resId, AppInfo[] items) { 35 | super(context, R.layout.list_item, items); 36 | mResource = resId; 37 | } 38 | 39 | @NonNull 40 | @Override 41 | public View getView(int position, View convertView, @NonNull ViewGroup parent) { 42 | View view; 43 | 44 | if (convertView == null) { 45 | view = View.inflate(getContext(), mResource, null); 46 | } else { 47 | view = convertView; 48 | } 49 | ImageView packageImage = (ImageView) view.findViewById(R.id.application_icon); 50 | TextView packageName = (TextView) view.findViewById(R.id.application_name); 51 | AppInfo appInfo = getItem(position); 52 | 53 | if (appInfo != null) { 54 | view.setTag(appInfo); 55 | packageName.setText(appInfo.getName()); 56 | if (appInfo.getIcon() != null) 57 | packageImage.setImageDrawable(appInfo.getIcon()); 58 | } 59 | return (view); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/application.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 30 | 31 | 39 | 40 | 55 | -------------------------------------------------------------------------------- /launcher/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 34 | 35 | 44 | 45 | 51 | 52 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/gridview.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 25 | 36 | 37 | 42 | 43 | 50 | 51 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/listview.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 25 | 36 | 37 | 42 | 43 | 50 | 51 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /launcher/src/main/res/values/notrans.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | Simple TV Launcher 20 | 00:00 21 | 42% 22 | Sunday 1, January 23 | Application 24 | 0.4 25 | Github 26 | 27 | 28 | 2 29 | 3 30 | 4 31 | 5 32 | 6 33 | 34 | 35 | 1 36 | 2 37 | 3 38 | 39 | 40 | 5 41 | 10 42 | 15 43 | 20 44 | 25 45 | 30 46 | 47 | 48 | 0% 49 | 5% 50 | 10% 51 | 15% 52 | 20% 53 | 25% 54 | 30% 55 | 45% 56 | 40% 57 | 55% 58 | 50% 59 | 65% 60 | 60% 61 | 75% 62 | 70% 63 | 85% 64 | 80% 65 | 95% 66 | 90% 67 | 100% 68 | 69 | 70 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 关于 20 | 行数 21 | 列数 22 | 系统 23 | 桌面背景 24 | 设置桌面背景图案 25 | 系统设置 26 | 打开系统设置 27 | 显示日期 28 | 显示名称 29 | 共 %s 列 30 | 共 %s 行 31 | 在时间下方显示日期 32 | 在主屏幕显示电池电量和状况 33 | 显示电池信息 34 | 在主屏幕显示应用名称 35 | 背景透明度 36 | 默认透明度 37 | 格子尺寸 38 | 保持屏幕常亮 39 | 屏幕常亮 40 | 锁定图标 41 | 防止图标被意外移除 42 | 图标已通过设置设为锁定 43 | 图标横向边距 44 | 图标 Y 边距 45 | 图标横向空白宽度:%s dp 46 | 图标纵向空白高度:%s dp 47 | 使用旧式图标背景透明度 48 | 错误 (%1$s) : %2$s 49 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/Launcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv; 19 | 20 | import android.os.Build; 21 | import android.os.Bundle; 22 | import android.support.v4.app.FragmentActivity; 23 | import android.view.View; 24 | import android.view.WindowManager; 25 | 26 | import org.cosinus.launchertv.fragments.ApplicationFragment; 27 | 28 | public class Launcher extends FragmentActivity { 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | 34 | setFullScreen(); 35 | setContentView(R.layout.activity_launcher); 36 | 37 | getSupportFragmentManager().beginTransaction() 38 | .replace(R.id.container, ApplicationFragment.newInstance(), ApplicationFragment.TAG) 39 | .commit(); 40 | } 41 | 42 | @Override 43 | protected void onResume() { 44 | super.onResume(); 45 | setFullScreen(); 46 | } 47 | 48 | private void setFullScreen() { 49 | try { 50 | if (Build.VERSION.SDK_INT < 19) { 51 | getWindow().setFlags( 52 | WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER, 53 | WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER 54 | ); 55 | } else { 56 | View decorView = getWindow().getDecorView(); 57 | 58 | int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE 59 | | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 60 | | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 61 | | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar 62 | | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar 63 | | View.SYSTEM_UI_FLAG_IMMERSIVE; 64 | 65 | decorView.setSystemUiVisibility(uiOptions); 66 | 67 | getWindow().setFlags( 68 | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER, 69 | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER 70 | ); 71 | } 72 | } catch (Exception e) { 73 | e.printStackTrace(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /launcher/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 23 | 26 | 29 | 32 | 35 | 38 | 41 | 44 | 45 | 47 | 48 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/Utils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv; 19 | 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.pm.PackageManager; 23 | import android.content.pm.ResolveInfo; 24 | import android.content.res.Resources; 25 | import android.util.TypedValue; 26 | 27 | import java.util.ArrayList; 28 | import java.util.Collections; 29 | import java.util.Comparator; 30 | import java.util.List; 31 | import java.util.Set; 32 | import java.util.HashSet; 33 | 34 | public class Utils { 35 | public static List loadApplications(Context context) { 36 | PackageManager packageManager = context.getPackageManager(); 37 | 38 | Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 39 | mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 40 | List intentActivities = packageManager.queryIntentActivities(mainIntent, 0); 41 | 42 | mainIntent = new Intent(Intent.ACTION_MAIN, null); 43 | mainIntent.addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER); 44 | intentActivities.addAll(packageManager.queryIntentActivities(mainIntent, 0)); 45 | 46 | Set knownPackages = new HashSet<>(); 47 | List entries = new ArrayList<>(); 48 | 49 | if (intentActivities != null) { 50 | for (ResolveInfo resolveInfo : intentActivities) { 51 | if (!context.getPackageName().equals(resolveInfo.activityInfo.packageName) && 52 | !knownPackages.contains(resolveInfo.activityInfo.packageName)) { 53 | entries.add(new AppInfo(packageManager, resolveInfo)); 54 | knownPackages.add(resolveInfo.activityInfo.packageName); 55 | } 56 | } 57 | } 58 | 59 | Collections.sort(entries, new Comparator() { 60 | @Override 61 | public int compare(AppInfo lhs, AppInfo rhs) { 62 | return lhs.getName().compareToIgnoreCase(rhs.getName()); 63 | } 64 | }); 65 | return entries; 66 | } 67 | 68 | public static int getPixelFromDp(Context context, int dp) { 69 | Resources r = context.getResources(); 70 | return ((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics())); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /launcher/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | Screen always on 20 | Keep screen on 21 | Grid size 22 | Columns 23 | Number of columns: %s 24 | Rows 25 | Number of rows: %s 26 | System 27 | Background 28 | Set background image 29 | System settings 30 | Open system settings 31 | Display the date below time 32 | Display battery level and status in home screen 33 | Display date 34 | Display battery information 35 | %1d%% 36 | Display applications names on main screen 37 | Display names 38 | Icons margins X 39 | Icons margins Y 40 | Width of space between icons: %s dp 41 | Height of space between icons: %s dp 42 | Background transparency 43 | Default transparency 44 | Use legacy tiles transparency 45 | About 46 | Error (%1$s) : %2$s 47 | Icon is locked by settings 48 | Prevent icons to be accidentally removed 49 | Lock icons 50 | 51 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-cs/strings.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | Obrazovka vždy zapnutá 23 | Udržovat obrazovku stále zapnutou 24 | Rozložení mřížky 25 | Sloupce 26 | Počet sloupců: %s 27 | Řádky 28 | Počet řádků: %s 29 | Systém 30 | Pozadí 31 | Nastavit obrázek pozadí 32 | Systémová nastavení 33 | Otevřít systémová nastavení 34 | Zobrazovat datum pod polem s časem 35 | Zobrazit datum 36 | Zobrazovat názvy aplikací na hlavní obrazovce 37 | Zobrazit názvy 38 | Okraje ikon ve směru osy X 39 | Okraje ikon ve směru osy Y 40 | Šířka volného místa mezi ikonami: %s dp 41 | Výška volného místa mezi ikonami: %s dp 42 | O aplikaci 43 | Použít starší dlažba průhlednost 44 | Použít starší dlažba průhlednost 45 | Průhlednost pozadí 46 | Error (%1$s) : %2$s 47 | Icon is locked by settings 48 | Prevent icons to be accidentally removed 49 | Lock icons 50 | Zobrazit informace o baterii 51 | Zobrazit stav baterie a stav na domovské obrazovce 52 | 53 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | A propos 20 | Taille de la grille 21 | Fond d\'écran 22 | Nombre de colonnes : %s 23 | Nombre de ligne : %s 24 | Largeur d\'espacement entre les icones : %s dp 25 | Hauteur d\'espacement entre les icones : %s dp 26 | Garder l\'écran allumer 27 | Afficher la date sous l\'heure 28 | Afficher le nom des applications sur l\'écran principal 29 | Ouvrir les paramètres Android 30 | Système 31 | Fond d\'écran 32 | Colonnes 33 | Lignes 34 | Marge X 35 | Marge Y 36 | Ecran toujours allumé 37 | Afficher la date 38 | Afficher les noms 39 | Paramètres système 40 | Utiliser la transparence par défaut (en cas de problème) 41 | Transparence par défaut 42 | Transparence du fond 43 | Error (%1$s) : %2$s 44 | Icone verrouillée (cf. Préférences) 45 | Empêcher la suppression accidentelle des icones 46 | Verrouiller les icones 47 | Afficher les informations sur la batterie 48 | Afficher le niveau et l\'état de la batterie sur l\'écran d\'accueil 49 | -------------------------------------------------------------------------------- /launcher/src/main/res/values-sk/strings.xml: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | Obrazovka vždy zapnutá 22 | Udržiavať obrazovku stále zapnutú 23 | Rozloženie mriežky 24 | Stĺpce 25 | Počet stĺpcov: %s 26 | Riadky 27 | Počet riadkov: %s 28 | Systém 29 | Pozadie 30 | Nastaviť obrázok pozadia 31 | Systémové nastavenia 32 | Otvoriť systémové nastavenia 33 | Zobrazovať dátum pod polom s časom 34 | Zobraziť dátum 35 | Zobrazovať názvy aplikácií na hlavnej obrazovke 36 | Zobraziť názvy 37 | Okraje ikon v smere osi X 38 | Okraje ikon v smere osi Y 39 | Šírka voľného miesta medzi ikonami: %s dp 40 | Výška voľného miesta medzi ikonami: %s dp 41 | O aplikácii 42 | Predvolené priehľadná 43 | Použiť staršie dlažba priehľadnosť 44 | Použiť staršie dlažba priehľadnosť 45 | Error (%1$s) : %2$s 46 | Icon is locked by settings 47 | Prevent icons to be accidentally removed 48 | Lock icons 49 | Zobraziť informácie o batérii 50 | Zobraziť stav batérie a stav na domovskej obrazovke 51 | 52 | -------------------------------------------------------------------------------- /launcher/build.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | apply plugin: 'com.android.application' 19 | apply plugin: 'com.github.kt3k.coveralls' 20 | apply plugin: 'checkstyle' 21 | 22 | checkstyle { 23 | toolVersion = '6.19' 24 | } 25 | 26 | check.dependsOn 'checkstyle' 27 | 28 | task checkstyle(type: Checkstyle) { 29 | configFile file("${project.rootDir}/style/checkstyle.xml") 30 | configProperties.checkstyleSuppressionsPath = file("${project.rootDir}/style/suppressions.xml").absolutePath 31 | 32 | source 'src/main/java' 33 | include 'org/cosinus/**/*.java' 34 | exclude '**/gen/**' 35 | 36 | def configProps = ['proj.module.dir': projectDir.absolutePath] 37 | configProperties configProps 38 | 39 | classpath = files() 40 | } 41 | 42 | android { 43 | compileSdkVersion 25 44 | buildToolsVersion '26.0.2' 45 | 46 | lintOptions { 47 | abortOnError true 48 | lintConfig file('lint.xml') 49 | } 50 | 51 | defaultConfig { 52 | applicationId "org.cosinus.launchertv" 53 | minSdkVersion 14 54 | targetSdkVersion 30 55 | versionCode 159 56 | versionName "1.5.9" 57 | } 58 | 59 | signingConfigs { 60 | if (project.hasProperty('keystorePassword')) { 61 | release { 62 | storeFile file(property('keystoreFile')) 63 | storePassword property('keystorePassword') 64 | keyAlias property('keystoreAlias') 65 | keyPassword property('keystorePassword') 66 | } 67 | } 68 | } 69 | 70 | buildTypes { 71 | release { 72 | debuggable false 73 | jniDebuggable false 74 | renderscriptDebuggable false 75 | minifyEnabled true 76 | proguardFile 'proguard-rules.pro' 77 | zipAlignEnabled true 78 | if (project.hasProperty('keystorePassword')) { 79 | //noinspection GroovyAssignabilityCheck 80 | signingConfig signingConfigs.release 81 | } 82 | } 83 | debug { 84 | applicationIdSuffix ".debug" 85 | testCoverageEnabled true 86 | } 87 | } 88 | } 89 | 90 | dependencies { 91 | implementation 'com.android.support:appcompat-v7:25.4.0' 92 | } 93 | 94 | coveralls { 95 | jacocoReportPath = "${buildDir}/reports/coverage/debug/report.xml" 96 | } 97 | 98 | tasks.coveralls { 99 | dependsOn 'connectedAndroidTest' 100 | onlyIf { System.env.'CI' } 101 | } 102 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/Setup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv; 19 | 20 | import android.content.Context; 21 | import android.content.SharedPreferences; 22 | import android.preference.PreferenceManager; 23 | 24 | import org.cosinus.launchertv.activities.Preferences; 25 | 26 | import static org.cosinus.launchertv.activities.Preferences.PREFERENCE_DEFAULT_TRANSPARENCY; 27 | import static org.cosinus.launchertv.activities.Preferences.PREFERENCE_TRANSPARENCY; 28 | 29 | 30 | public class Setup { 31 | private static final int DEFAULT_GRID_X = 3; 32 | private static final int DEFAULT_GRID_Y = 2; 33 | private static final int DEFAULT_MARGIN_X = 5; 34 | private static final int DEFAULT_MARGIN_Y = 5; 35 | 36 | private final Context mContext; 37 | private SharedPreferences mPreferences; 38 | 39 | public Setup(Context context) { 40 | mContext = context; 41 | } 42 | 43 | private SharedPreferences getPreferences() { 44 | if (mPreferences == null) { 45 | mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); 46 | } 47 | return (mPreferences); 48 | } 49 | 50 | private int getInt(String name, int defaultValue) { 51 | try { 52 | String value = getPreferences().getString(name, null); 53 | if (value != null) 54 | return (Integer.parseInt(value)); 55 | } catch (Exception e) { 56 | e.printStackTrace(); 57 | } 58 | return (defaultValue); 59 | } 60 | 61 | @SuppressWarnings("BooleanMethodIsAlwaysInverted") 62 | public boolean isDefaultTransparency() { 63 | try { 64 | return (getPreferences().getBoolean(PREFERENCE_DEFAULT_TRANSPARENCY, true)); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | return (true); 69 | } 70 | 71 | public float getTransparency() { 72 | try { 73 | return (getPreferences().getFloat(PREFERENCE_TRANSPARENCY, 0.5F)); 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | } 77 | return (0.5F); 78 | } 79 | 80 | public boolean keepScreenOn() { 81 | try { 82 | return (getPreferences().getBoolean(Preferences.PREFERENCE_SCREEN_ON, false)); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | } 86 | return (false); 87 | } 88 | 89 | public boolean iconsLocked() { 90 | try { 91 | return (getPreferences().getBoolean(Preferences.PREFERENCE_LOCKED, false)); 92 | } catch (Exception e) { 93 | e.printStackTrace(); 94 | } 95 | return (false); 96 | } 97 | 98 | public boolean showDate() { 99 | try { 100 | return (getPreferences().getBoolean(Preferences.PREFERENCE_SHOW_DATE, true)); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | } 104 | return (true); 105 | } 106 | 107 | public boolean showBattery() { 108 | try { 109 | return (getPreferences().getBoolean(Preferences.PREFERENCE_SHOW_BATTERY, false)); 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | } 113 | return (false); 114 | } 115 | 116 | public boolean showNames() { 117 | try { 118 | return (getPreferences().getBoolean(Preferences.PREFERENCE_SHOW_NAME, true)); 119 | } catch (Exception e) { 120 | e.printStackTrace(); 121 | } 122 | return (true); 123 | } 124 | 125 | 126 | public int getGridX() { 127 | return getInt(Preferences.PREFERENCE_GRID_X, DEFAULT_GRID_X); 128 | } 129 | 130 | public int getGridY() { 131 | return getInt(Preferences.PREFERENCE_GRID_Y, DEFAULT_GRID_Y); 132 | } 133 | 134 | public int getMarginX() { 135 | return getInt(Preferences.PREFERENCE_MARGIN_X, DEFAULT_MARGIN_X); 136 | } 137 | 138 | public int getMarginY() { 139 | return getInt(Preferences.PREFERENCE_MARGIN_Y, DEFAULT_MARGIN_Y); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/views/SliderPreference.java: -------------------------------------------------------------------------------- 1 | package org.cosinus.launchertv.views; 2 | 3 | /* 4 | * Copyright 2012 Jay Weisskopf 5 | * 6 | * Licensed under the MIT License (see LICENSE.txt) 7 | */ 8 | 9 | 10 | import android.content.Context; 11 | import android.content.res.TypedArray; 12 | import android.preference.DialogPreference; 13 | import android.util.AttributeSet; 14 | import android.view.View; 15 | import android.widget.SeekBar; 16 | 17 | import org.cosinus.launchertv.R; 18 | 19 | /** 20 | * @author Jay Weisskopf 21 | */ 22 | @SuppressWarnings("ALL") 23 | public class SliderPreference extends DialogPreference { 24 | 25 | protected final static int SEEKBAR_RESOLUTION = 10000; 26 | 27 | protected float mValue; 28 | protected int mSeekBarValue; 29 | protected CharSequence[] mSummaries; 30 | 31 | /** 32 | * @param context 33 | * @param attrs 34 | */ 35 | public SliderPreference(Context context, AttributeSet attrs) { 36 | super(context, attrs); 37 | setup(context, attrs); 38 | } 39 | 40 | /** 41 | * @param context 42 | * @param attrs 43 | * @param defStyle 44 | */ 45 | public SliderPreference(Context context, AttributeSet attrs, int defStyle) { 46 | super(context, attrs, defStyle); 47 | setup(context, attrs); 48 | } 49 | 50 | private void setup(Context context, AttributeSet attrs) { 51 | setDialogLayoutResource(R.layout.slider_preference_dialog); 52 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SliderPreference); 53 | try { 54 | setSummary(a.getTextArray(R.styleable.SliderPreference_android_summary)); 55 | } catch (Exception e) { 56 | // Do nothing 57 | } 58 | a.recycle(); 59 | } 60 | 61 | @Override 62 | protected Object onGetDefaultValue(TypedArray a, int index) { 63 | return a.getFloat(index, 0); 64 | } 65 | 66 | @Override 67 | protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 68 | setValue(restoreValue ? getPersistedFloat(mValue) : (Float) defaultValue); 69 | } 70 | 71 | @Override 72 | public CharSequence getSummary() { 73 | if (mSummaries != null && mSummaries.length > 0) { 74 | int index = (int) (mValue * mSummaries.length); 75 | index = Math.min(index, mSummaries.length - 1); 76 | return mSummaries[index]; 77 | } else { 78 | return super.getSummary(); 79 | } 80 | } 81 | 82 | public void setSummary(CharSequence[] summaries) { 83 | mSummaries = summaries; 84 | } 85 | 86 | @Override 87 | public void setSummary(CharSequence summary) { 88 | super.setSummary(summary); 89 | mSummaries = null; 90 | } 91 | 92 | @Override 93 | public void setSummary(int summaryResId) { 94 | try { 95 | this.setSummary(getContext().getResources().getStringArray(summaryResId)); 96 | } catch (Exception e) { 97 | super.setSummary(summaryResId); 98 | } 99 | } 100 | 101 | public void setValue(float value) { 102 | value = Math.max(0, Math.min(value, 1)); // clamp to [0, 1] 103 | if (shouldPersist()) { 104 | persistFloat(value); 105 | } 106 | if (value != mValue) { 107 | mValue = value; 108 | notifyChanged(); 109 | } 110 | } 111 | 112 | @Override 113 | protected View onCreateDialogView() { 114 | mSeekBarValue = (int) (mValue * SEEKBAR_RESOLUTION); 115 | View view = super.onCreateDialogView(); 116 | SeekBar seekbar = (SeekBar) view.findViewById(R.id.slider_preference_seekbar); 117 | seekbar.setMax(SEEKBAR_RESOLUTION); 118 | seekbar.setProgress(mSeekBarValue); 119 | seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 120 | 121 | @Override 122 | public void onStopTrackingTouch(SeekBar seekBar) { 123 | } 124 | 125 | @Override 126 | public void onStartTrackingTouch(SeekBar seekBar) { 127 | } 128 | 129 | @Override 130 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 131 | if (fromUser) { 132 | SliderPreference.this.mSeekBarValue = progress; 133 | } 134 | } 135 | }); 136 | return view; 137 | } 138 | 139 | @Override 140 | protected void onDialogClosed(boolean positiveResult) { 141 | final float newValue = (float) mSeekBarValue / SEEKBAR_RESOLUTION; 142 | if (positiveResult && callChangeListener(newValue)) { 143 | setValue(newValue); 144 | } 145 | super.onDialogClosed(positiveResult); 146 | } 147 | 148 | // TODO: Save and restore preference state. 149 | } 150 | -------------------------------------------------------------------------------- /launcher/src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 25 | 30 | 35 | 40 | 45 | 46 | 49 | 56 | 63 | 70 | 77 | 78 | 81 | 86 | 92 | 93 | 94 | 98 | 99 | 100 | 104 | 105 | 106 | 107 | 108 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/activities/ApplicationList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv.activities; 19 | 20 | import android.app.Activity; 21 | import android.content.Intent; 22 | import android.os.AsyncTask; 23 | import android.os.Bundle; 24 | import android.view.View; 25 | import android.widget.AbsListView; 26 | import android.widget.AdapterView; 27 | 28 | import org.cosinus.launchertv.AppInfo; 29 | import org.cosinus.launchertv.R; 30 | import org.cosinus.launchertv.Utils; 31 | import org.cosinus.launchertv.views.ApplicationAdapter; 32 | 33 | 34 | public class ApplicationList extends Activity implements AdapterView.OnItemClickListener, View.OnClickListener { 35 | public static final String PACKAGE_NAME = "package_name"; 36 | public static final String APPLICATION_NUMBER = "application"; 37 | public static final String VIEW_TYPE = "view_type"; 38 | public static final String DELETE = "delete"; 39 | public static final String SHOW_DELETE = "show_delete"; 40 | // 41 | public static final int VIEW_GRID = 0; 42 | public static final int VIEW_LIST = 1; 43 | // 44 | private int mApplication = -1; 45 | private int mViewType = 0; 46 | private AbsListView mListView; 47 | private final AsyncTask mApplicationLoader = new AsyncTask() { 48 | @Override 49 | protected AppInfo[] doInBackground(Void... params) { 50 | return Utils.loadApplications(ApplicationList.this).toArray(new AppInfo[0]); 51 | } 52 | 53 | @Override 54 | protected void onPostExecute(AppInfo[] apps) { 55 | getListView().setOnItemClickListener(ApplicationList.this); 56 | getListView().setAdapter( 57 | new ApplicationAdapter(ApplicationList.this, 58 | mViewType == VIEW_LIST ? R.layout.list_item : R.layout.grid_item, 59 | apps)); 60 | } 61 | }; 62 | 63 | @Override 64 | protected void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | 67 | Intent intent = getIntent(); 68 | Bundle args = intent.getExtras(); 69 | 70 | if (args != null) { 71 | if (args.containsKey(APPLICATION_NUMBER)) 72 | mApplication = args.getInt(APPLICATION_NUMBER); 73 | if (args.containsKey(VIEW_TYPE)) 74 | mViewType = args.getInt(VIEW_TYPE); 75 | } 76 | 77 | setContentView(mViewType == VIEW_LIST ? 78 | R.layout.listview : 79 | R.layout.gridview); 80 | 81 | mListView = (AbsListView) findViewById(R.id.list); 82 | mApplicationLoader.execute(); 83 | 84 | View v; 85 | if ((args != null) && (args.containsKey(SHOW_DELETE))) { 86 | if (!args.getBoolean(SHOW_DELETE)) { 87 | if ((v = findViewById(R.id.bottom_panel)) != null) 88 | v.setVisibility(View.GONE); 89 | } 90 | } 91 | if ((v = findViewById(R.id.delete)) != null) 92 | v.setOnClickListener(this); 93 | if ((v = findViewById(R.id.cancel)) != null) 94 | v.setOnClickListener(this); 95 | } 96 | 97 | private AbsListView getListView() { 98 | return mListView; 99 | } 100 | 101 | @Override 102 | public void onItemClick(AdapterView parent, View view, int position, long id) { 103 | AppInfo appInfo = (AppInfo) view.getTag(); 104 | Intent data = new Intent(); 105 | 106 | data.putExtra(PACKAGE_NAME, appInfo.getPackageName()); 107 | data.putExtra(APPLICATION_NUMBER, mApplication); 108 | 109 | if (getParent() == null) { 110 | setResult(Activity.RESULT_OK, data); 111 | } else { 112 | getParent().setResult(Activity.RESULT_OK, data); 113 | } 114 | finish(); 115 | } 116 | 117 | @Override 118 | public void onClick(View v) { 119 | switch (v.getId()) { 120 | case R.id.delete: 121 | Intent data = new Intent(); 122 | 123 | data.putExtra(DELETE, true); 124 | data.putExtra(APPLICATION_NUMBER, mApplication); 125 | 126 | if (getParent() == null) 127 | setResult(Activity.RESULT_OK, data); 128 | else 129 | getParent().setResult(Activity.RESULT_OK, data); 130 | finish(); 131 | break; 132 | 133 | case R.id.cancel: 134 | if (getParent() == null) 135 | setResult(Activity.RESULT_CANCELED); 136 | else 137 | getParent().setResult(Activity.RESULT_CANCELED); 138 | finish(); 139 | break; 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /launcher/src/main/res/layout/fragment_application.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 28 | 29 | 33 | 34 | 41 | 42 | 54 | 55 | 60 | 72 | 73 | 81 | 82 | 83 | 84 | 85 | 86 | 96 | 97 | 110 | 111 | 125 | 126 | 127 | 128 | 129 | 137 | 138 | 139 | 140 | 144 | 145 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/activities/Preferences.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv.activities; 19 | 20 | import android.app.Activity; 21 | import android.content.Intent; 22 | import android.content.pm.PackageInfo; 23 | import android.content.pm.PackageManager; 24 | import android.net.Uri; 25 | import android.os.Bundle; 26 | import android.preference.ListPreference; 27 | import android.preference.Preference; 28 | import android.preference.PreferenceActivity; 29 | import android.widget.Toast; 30 | 31 | import org.cosinus.launchertv.R; 32 | import org.cosinus.launchertv.Setup; 33 | 34 | import java.util.Locale; 35 | 36 | @SuppressWarnings("deprecation") 37 | public class Preferences extends PreferenceActivity { 38 | public static final String PREFERENCE_DEFAULT_TRANSPARENCY = "preference_default_transparency"; 39 | public static final String PREFERENCE_TRANSPARENCY = "preference_transparency"; 40 | public static final String PREFERENCE_SCREEN_ON = "preference_screen_always_on"; 41 | public static final String PREFERENCE_SHOW_DATE = "preference_show_date"; 42 | public static final String PREFERENCE_SHOW_BATTERY = "preference_show_battery"; 43 | public static final String PREFERENCE_GRID_X = "preference_grid_x"; 44 | public static final String PREFERENCE_GRID_Y = "preference_grid_y"; 45 | public static final String PREFERENCE_SHOW_NAME = "preference_show_name"; 46 | public static final String PREFERENCE_MARGIN_X = "preference_margin_x"; 47 | public static final String PREFERENCE_MARGIN_Y = "preference_margin_y"; 48 | public static final String PREFERENCE_LOCKED = "preference_locked"; 49 | private static final String PREFERENCE_GITHUB = "preference_github"; 50 | private static final String PREFERENCE_ABOUT = "preference_about"; 51 | 52 | @Override 53 | protected void onCreate(Bundle savedInstanceState) { 54 | super.onCreate(savedInstanceState); 55 | 56 | Setup setup = new Setup(this); 57 | addPreferencesFromResource(R.xml.preferences); 58 | 59 | bindSummary(PREFERENCE_GRID_X, R.string.summary_grid_x); 60 | bindSummary(PREFERENCE_GRID_Y, R.string.summary_grid_y); 61 | bindSummary(PREFERENCE_MARGIN_X, R.string.summary_margin_x); 62 | bindSummary(PREFERENCE_MARGIN_Y, R.string.summary_margin_y); 63 | 64 | findPreference(PREFERENCE_TRANSPARENCY).setEnabled(!setup.isDefaultTransparency()); 65 | findPreference(PREFERENCE_DEFAULT_TRANSPARENCY).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 66 | @Override 67 | public boolean onPreferenceChange(Preference preference, Object newValue) { 68 | findPreference(PREFERENCE_TRANSPARENCY).setEnabled(!(boolean) newValue); 69 | return true; 70 | } 71 | }); 72 | 73 | findPreference(PREFERENCE_GITHUB).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 74 | @Override 75 | public boolean onPreferenceClick(Preference preference) { 76 | try { 77 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/alescdb/LauncherTV"))); 78 | } catch (Exception e) { 79 | e.printStackTrace(); 80 | Toast.makeText(getApplicationContext(), 81 | String.format(getString(R.string.error_opening_link), "Github", e.getMessage()), 82 | Toast.LENGTH_LONG).show(); 83 | } 84 | return (true); 85 | } 86 | }); 87 | 88 | PackageInfo pInfo; 89 | String version = "#Err"; 90 | try { 91 | pInfo = getPackageManager().getPackageInfo(getPackageName(), 0); 92 | version = pInfo.versionName; 93 | } catch (PackageManager.NameNotFoundException e) { 94 | e.printStackTrace(); 95 | } 96 | 97 | findPreference(PREFERENCE_ABOUT).setTitle(getString(R.string.app_name) + " version " + version); 98 | findPreference(PREFERENCE_ABOUT).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 99 | @Override 100 | public boolean onPreferenceClick(Preference preference) { 101 | try { 102 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=org.cosinus.launchertv"))); 103 | } catch (Exception e) { 104 | e.printStackTrace(); 105 | Toast.makeText(getApplicationContext(), 106 | String.format(getString(R.string.error_opening_link), "Play Store", e.getMessage()), 107 | Toast.LENGTH_LONG).show(); 108 | } 109 | return (true); 110 | } 111 | }); 112 | } 113 | 114 | private void bindSummary(String key, final int resId) { 115 | final ListPreference p = (ListPreference) findPreference(key); 116 | setPreferenceSummaryValue(p, resId, p.getValue()); 117 | p.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 118 | @Override 119 | public boolean onPreferenceChange(Preference preference, Object newValue) { 120 | setPreferenceSummaryValue(p, resId, (String) newValue); 121 | return true; 122 | } 123 | }); 124 | } 125 | 126 | private void setPreferenceSummaryValue(ListPreference prefs, int resId, String value) { 127 | prefs.setSummary( 128 | String.format(Locale.getDefault(), getString(resId), value) 129 | ); 130 | } 131 | 132 | @Override 133 | public void onDestroy() { 134 | if (getParent() == null) { 135 | setResult(Activity.RESULT_OK, null); 136 | } else { 137 | getParent().setResult(Activity.RESULT_OK, null); 138 | } 139 | super.onDestroy(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/views/ApplicationView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv.views; 19 | 20 | import android.content.Context; 21 | import android.graphics.Color; 22 | import android.graphics.drawable.Drawable; 23 | import android.graphics.drawable.GradientDrawable; 24 | import android.graphics.drawable.StateListDrawable; 25 | import android.os.Build; 26 | import android.support.annotation.DrawableRes; 27 | import android.text.TextUtils; 28 | import android.util.AttributeSet; 29 | import android.util.Log; 30 | import android.util.StateSet; 31 | import android.view.KeyEvent; 32 | import android.widget.ImageView; 33 | import android.widget.LinearLayout; 34 | import android.widget.TextView; 35 | 36 | import org.cosinus.launchertv.R; 37 | import org.cosinus.launchertv.Setup; 38 | 39 | import java.util.Locale; 40 | 41 | import static android.content.ContentValues.TAG; 42 | 43 | public class ApplicationView extends LinearLayout { 44 | private OnClickListener mMenuClickListener; 45 | private ImageView mIcon; 46 | private TextView mText; 47 | private String mPackageName; 48 | private int mPosition; 49 | 50 | public ApplicationView(Context context) { 51 | super(context); 52 | initialize(context, null, null); 53 | } 54 | 55 | public ApplicationView(Context context, AttributeSet attrs) { 56 | super(context, attrs); 57 | initialize(context, attrs, null); 58 | } 59 | 60 | public ApplicationView(Context context, AttributeSet attrs, int defStyle) { 61 | super(context, attrs, defStyle); 62 | initialize(context, attrs, defStyle); 63 | } 64 | 65 | public static String getPreferenceKey(int appNum) { 66 | return (String.format(Locale.getDefault(), "application_%02d", appNum)); 67 | } 68 | 69 | private static Drawable createTileShape(int backgroundColor, int borderColor) { 70 | GradientDrawable shape = new GradientDrawable(); 71 | shape.setShape(GradientDrawable.RECTANGLE); 72 | shape.setCornerRadii(new float[]{7, 7, 7, 7, 0, 0, 0, 0}); 73 | shape.setColor(backgroundColor); 74 | shape.setStroke(1, borderColor); 75 | shape.setBounds(7, 7, 7, 7); 76 | return (shape); 77 | } 78 | 79 | 80 | @Override 81 | public boolean onKeyDown(int keyCode, KeyEvent event) { 82 | Log.d(TAG, "keyCode => " + keyCode); 83 | if (keyCode == KeyEvent.KEYCODE_MENU) { 84 | if (mMenuClickListener != null) { 85 | mMenuClickListener.onClick(this); 86 | } 87 | return true; 88 | } 89 | return super.onKeyDown(keyCode, event); 90 | } 91 | 92 | public void setOnMenuOnClickListener(OnClickListener clickListener) { 93 | mMenuClickListener = clickListener; 94 | } 95 | 96 | private void setBackgroundStateDrawable(float transparency) { 97 | StateListDrawable stateListDrawable = new StateListDrawable(); 98 | 99 | Drawable drawableEnabled = createTileShape( 100 | Color.argb(getTransparency(transparency, 0.0F), 0xF0, 0xF0, 0xF0), 101 | Color.argb(0xFF, 0x90, 0x90, 0x90) 102 | ); 103 | Drawable drawableFocused = createTileShape( 104 | Color.argb(getTransparency(transparency, 0.4F), 0xE0, 0xE0, 0xFF), 105 | Color.argb(0xFF, 0x90, 0x90, 0x90) 106 | ); 107 | Drawable drawablePressed = createTileShape( 108 | Color.argb(getTransparency(transparency, 0.8F), 0xE0, 0xE0, 0xFF), 109 | Color.argb(0xFF, 0x00, 0x00, 0x00) 110 | ); 111 | 112 | stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, drawablePressed); 113 | stateListDrawable.addState(new int[]{android.R.attr.state_focused}, drawableFocused); 114 | stateListDrawable.addState(new int[]{android.R.attr.state_hovered}, drawableFocused); 115 | stateListDrawable.addState(StateSet.WILD_CARD, drawableEnabled); 116 | 117 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 118 | setBackground(stateListDrawable); 119 | } else { 120 | //noinspection deprecation 121 | setBackgroundDrawable(stateListDrawable); 122 | } 123 | } 124 | 125 | private int getTransparency(float transparency, float add) { 126 | int trans = (int) ((transparency + add) * 255.0); 127 | if (trans > 255) 128 | return (255); 129 | if (trans < 0) 130 | return (0); 131 | return (trans); 132 | } 133 | 134 | @SuppressWarnings("UnusedParameters") 135 | private void initialize(Context context, AttributeSet attrs, Integer defStyle) { 136 | Setup setup = new Setup(context); 137 | 138 | inflate(context, R.layout.application, this); 139 | 140 | setClickable(true); 141 | setFocusable(true); 142 | 143 | if (!setup.isDefaultTransparency()) { 144 | setBackgroundStateDrawable(setup.getTransparency()); 145 | } else { 146 | setBackgroundResource(R.drawable.application_selector); 147 | } 148 | 149 | mIcon = (ImageView) findViewById(R.id.application_icon); 150 | mText = (TextView) findViewById(R.id.application_name); 151 | } 152 | 153 | @SuppressWarnings("SameParameterValue") 154 | public ApplicationView setImageResource(@DrawableRes int res) { 155 | mIcon.setImageResource(res); 156 | return (this); 157 | } 158 | 159 | public ApplicationView setImageDrawable(Drawable drawable) { 160 | mIcon.setImageDrawable(drawable); 161 | return (this); 162 | } 163 | 164 | public ApplicationView setText(CharSequence text) { 165 | mText.setText(text); 166 | return (this); 167 | } 168 | 169 | public void showName(boolean show) { 170 | mText.setVisibility( 171 | show ? VISIBLE : GONE 172 | ); 173 | } 174 | 175 | public String getPackageName() { 176 | return mPackageName; 177 | } 178 | 179 | @SuppressWarnings("UnusedReturnValue") 180 | public ApplicationView setPackageName(String packageName) { 181 | mPackageName = packageName; 182 | return (this); 183 | } 184 | 185 | public String getName() { 186 | return mText.getText().toString(); 187 | } 188 | 189 | public boolean hasPackage() { 190 | return !TextUtils.isEmpty(mPackageName); 191 | } 192 | 193 | public int getPosition() { 194 | return mPosition; 195 | } 196 | 197 | public void setPosition(int position) { 198 | mPosition = position; 199 | } 200 | 201 | public String getPreferenceKey() { 202 | return (getPreferenceKey(getPosition())); 203 | } 204 | 205 | } 206 | -------------------------------------------------------------------------------- /LauncherTV (Code Style).xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 19 | 41 | -------------------------------------------------------------------------------- /.idea/codeStyleSettings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 257 | 259 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /launcher/src/main/java/org/cosinus/launchertv/fragments/ApplicationFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple TV Launcher 3 | * Copyright 2017 Alexandre Del Bigio 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.cosinus.launchertv.fragments; 19 | 20 | import android.app.Activity; 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.content.IntentFilter; 25 | import android.content.SharedPreferences; 26 | import android.content.pm.PackageInfo; 27 | import android.content.pm.PackageManager; 28 | import android.os.BatteryManager; 29 | import android.os.Build; 30 | import android.os.Bundle; 31 | import android.os.Handler; 32 | import android.support.v4.app.Fragment; 33 | import android.text.TextUtils; 34 | import android.view.Gravity; 35 | import android.view.LayoutInflater; 36 | import android.view.View; 37 | import android.view.ViewGroup; 38 | import android.widget.ImageView; 39 | import android.widget.LinearLayout; 40 | import android.widget.TextView; 41 | import android.widget.Toast; 42 | 43 | import org.cosinus.launchertv.AppInfo; 44 | import org.cosinus.launchertv.R; 45 | import org.cosinus.launchertv.Setup; 46 | import org.cosinus.launchertv.Utils; 47 | import org.cosinus.launchertv.activities.ApplicationList; 48 | import org.cosinus.launchertv.activities.Preferences; 49 | import org.cosinus.launchertv.views.ApplicationView; 50 | 51 | import java.text.DateFormat; 52 | import java.util.Date; 53 | 54 | @SuppressWarnings("PointlessBooleanExpression") 55 | public class ApplicationFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener { 56 | public static final String TAG = "ApplicationFragment"; 57 | private static final String PREFERENCES_NAME = "applications"; 58 | private static final int REQUEST_CODE_APPLICATION_LIST = 0x1E; 59 | private static final int REQUEST_CODE_WALLPAPER = 0x1F; 60 | private static final int REQUEST_CODE_APPLICATION_START = 0x20; 61 | private static final int REQUEST_CODE_PREFERENCES = 0x21; 62 | 63 | private TextView mClock; 64 | private TextView mDate; 65 | private DateFormat mTimeFormat; 66 | private DateFormat mDateFormat; 67 | private TextView mBatteryLevel; 68 | private ImageView mBatteryIcon; 69 | private BroadcastReceiver mBatteryChangedReceiver = new BroadcastReceiver(){ 70 | @Override 71 | public void onReceive(Context context, Intent intent) { 72 | final int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); 73 | mBatteryLevel.setText( 74 | String.format(getResources().getString(R.string.battery_level_text), level) 75 | ); 76 | final int batteryIconId = intent.getIntExtra(BatteryManager.EXTRA_ICON_SMALL, 0); 77 | mBatteryIcon.setImageDrawable(getResources().getDrawable(batteryIconId)); 78 | } 79 | }; 80 | private boolean mBatteryChangedReceiverRegistered = false; 81 | 82 | private final Handler mHandler = new Handler(); 83 | private final Runnable mTimerTick = new Runnable() { 84 | @Override 85 | public void run() { 86 | setClock(); 87 | } 88 | }; 89 | 90 | private int mGridX = 3; 91 | private int mGridY = 2; 92 | private LinearLayout mContainer; 93 | private ApplicationView[][] mApplications = null; 94 | private View mSettings; 95 | private View mGridView; 96 | private Setup mSetup; 97 | 98 | 99 | public ApplicationFragment() { 100 | // Required empty public constructor 101 | } 102 | 103 | public static ApplicationFragment newInstance() { 104 | return new ApplicationFragment(); 105 | } 106 | 107 | @Override 108 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 109 | View view = inflater.inflate(R.layout.fragment_application, container, false); 110 | 111 | mSetup = new Setup(getContext()); 112 | mContainer = (LinearLayout) view.findViewById(R.id.container); 113 | mSettings = view.findViewById(R.id.settings); 114 | mGridView = view.findViewById(R.id.application_grid); 115 | mClock = (TextView) view.findViewById(R.id.clock); 116 | mDate = (TextView) view.findViewById(R.id.date); 117 | final LinearLayout batteryLayout = (LinearLayout) view.findViewById(R.id.battery_layout); 118 | mBatteryLevel = (TextView) view.findViewById(R.id.battery_level); 119 | mBatteryIcon = (ImageView) view.findViewById(R.id.battery_icon); 120 | 121 | mTimeFormat = android.text.format.DateFormat.getTimeFormat(getActivity()); 122 | mDateFormat = android.text.format.DateFormat.getLongDateFormat(getActivity()); 123 | 124 | if (mSetup.keepScreenOn()) 125 | mContainer.setKeepScreenOn(true); 126 | 127 | if (mSetup.showDate() == false) 128 | mDate.setVisibility(View.GONE); 129 | 130 | if (mSetup.showBattery()) { 131 | batteryLayout.setVisibility(View.VISIBLE); 132 | getActivity().registerReceiver(this.mBatteryChangedReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 133 | mBatteryChangedReceiverRegistered = true; 134 | } else { 135 | batteryLayout.setVisibility(View.INVISIBLE); 136 | if (mBatteryChangedReceiverRegistered) { 137 | getActivity().unregisterReceiver(this.mBatteryChangedReceiver); 138 | mBatteryChangedReceiverRegistered = false; 139 | } 140 | } 141 | 142 | mSettings.setOnClickListener(this); 143 | mGridView.setOnClickListener(this); 144 | 145 | createApplications(); 146 | 147 | return view; 148 | } 149 | 150 | private void createApplications() { 151 | mContainer.removeAllViews(); 152 | 153 | mGridX = mSetup.getGridX(); 154 | mGridY = mSetup.getGridY(); 155 | 156 | if (mGridX < 2) 157 | mGridX = 2; 158 | if (mGridY < 1) 159 | mGridY = 1; 160 | 161 | int marginX = Utils.getPixelFromDp(getContext(), mSetup.getMarginX()); 162 | int marginY = Utils.getPixelFromDp(getContext(), mSetup.getMarginY()); 163 | 164 | boolean showNames = mSetup.showNames(); 165 | 166 | mApplications = new ApplicationView[mGridY][mGridX]; 167 | 168 | int position = 0; 169 | for (int y = 0; y < mGridY; y++) { 170 | LinearLayout ll = new LinearLayout(getContext()); 171 | ll.setOrientation(LinearLayout.HORIZONTAL); 172 | ll.setGravity(Gravity.CENTER_VERTICAL); 173 | ll.setFocusable(false); 174 | ll.setLayoutParams(new LinearLayout.LayoutParams( 175 | LinearLayout.LayoutParams.MATCH_PARENT, 0, 1 176 | )); 177 | 178 | for (int x = 0; x < mGridX; x++) { 179 | ApplicationView av = new ApplicationView(getContext()); 180 | av.setOnClickListener(this); 181 | av.setOnLongClickListener(this); 182 | av.setOnMenuOnClickListener(new View.OnClickListener() { 183 | @Override 184 | public void onClick(View v) { 185 | onLongClick(v); 186 | } 187 | }); 188 | av.setPosition(position++); 189 | av.showName(showNames); 190 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { 191 | av.setId(0x00FFFFFF + position); 192 | } else { 193 | av.setId(View.generateViewId()); 194 | } 195 | LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1); 196 | lp.setMargins(marginX, marginY, marginX, marginY); 197 | av.setLayoutParams(lp); 198 | ll.addView(av); 199 | mApplications[y][x] = av; 200 | } 201 | mContainer.addView(ll); 202 | } 203 | 204 | updateApplications(); 205 | setApplicationOrder(); 206 | } 207 | 208 | private void setApplicationOrder() { 209 | for (int y = 0; y < mGridY; y++) { 210 | for (int x = 0; x < mGridX; x++) { 211 | int upId = R.id.application_grid; 212 | int downId = R.id.settings; 213 | int leftId = R.id.application_grid; 214 | int rightId = R.id.settings; 215 | 216 | if (y > 0) 217 | upId = mApplications[y - 1][x].getId(); 218 | 219 | if (y + 1 < mGridY) 220 | downId = mApplications[y + 1][x].getId(); 221 | 222 | if (x > 0) 223 | leftId = mApplications[y][x - 1].getId(); 224 | else if (y > 0) 225 | leftId = mApplications[y - 1][mGridX - 1].getId(); 226 | 227 | if (x + 1 < mGridX) 228 | rightId = mApplications[y][x + 1].getId(); 229 | else if (y + 1 < mGridY) 230 | rightId = mApplications[y + 1][0].getId(); 231 | 232 | mApplications[y][x].setNextFocusLeftId(leftId); 233 | mApplications[y][x].setNextFocusRightId(rightId); 234 | mApplications[y][x].setNextFocusUpId(upId); 235 | mApplications[y][x].setNextFocusDownId(downId); 236 | } 237 | } 238 | 239 | mGridView.setNextFocusLeftId(R.id.settings); 240 | mGridView.setNextFocusRightId(mApplications[0][0].getId()); 241 | mGridView.setNextFocusUpId(R.id.settings); 242 | mGridView.setNextFocusDownId(mApplications[0][0].getId()); 243 | 244 | mSettings.setNextFocusLeftId(mApplications[mGridY - 1][mGridX - 1].getId()); 245 | mSettings.setNextFocusRightId(R.id.application_grid); 246 | mSettings.setNextFocusUpId(mApplications[mGridY - 1][mGridX - 1].getId()); 247 | mSettings.setNextFocusDownId(R.id.application_grid); 248 | } 249 | 250 | 251 | private void updateApplications() { 252 | PackageManager pm = getActivity().getPackageManager(); 253 | SharedPreferences prefs = getActivity().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); 254 | 255 | for (int y = 0; y < mGridY; y++) { 256 | for (int x = 0; x < mGridX; x++) { 257 | ApplicationView app = mApplications[y][x]; 258 | setApplication(pm, app, prefs.getString(app.getPreferenceKey(), null)); 259 | } 260 | } 261 | } 262 | 263 | 264 | private void restartActivity() { 265 | if (mBatteryChangedReceiverRegistered) { 266 | getActivity().unregisterReceiver(mBatteryChangedReceiver); 267 | mBatteryChangedReceiverRegistered = false; 268 | } 269 | Intent intent = getActivity().getIntent(); 270 | getActivity().finish(); 271 | startActivity(intent); 272 | } 273 | 274 | 275 | private void writePreferences(int appNum, String packageName) { 276 | SharedPreferences prefs = getActivity().getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE); 277 | SharedPreferences.Editor editor = prefs.edit(); 278 | String key = ApplicationView.getPreferenceKey(appNum); 279 | 280 | if (TextUtils.isEmpty(packageName)) 281 | editor.remove(key); 282 | else 283 | editor.putString(key, packageName); 284 | 285 | editor.apply(); 286 | } 287 | 288 | private void setApplication(PackageManager pm, ApplicationView app, String packageName) { 289 | try { 290 | 291 | if (TextUtils.isEmpty(packageName) == false) { 292 | PackageInfo pi = pm.getPackageInfo(packageName, 0); 293 | if (pi != null) { 294 | AppInfo appInfo = new AppInfo(pm, pi.applicationInfo); 295 | app.setImageDrawable(appInfo.getIcon()) 296 | .setText(appInfo.getName()) 297 | .setPackageName(appInfo.getPackageName()); 298 | } 299 | } else { 300 | app.setImageResource(R.drawable.ic_add) 301 | .setText("") 302 | .setPackageName(null); 303 | } 304 | } catch (Exception e) { 305 | e.printStackTrace(); 306 | } 307 | } 308 | 309 | @Override 310 | public void onStart() { 311 | super.onStart(); 312 | setClock(); 313 | if (mSetup.showBattery() && !mBatteryChangedReceiverRegistered) { 314 | getActivity().registerReceiver(this.mBatteryChangedReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); 315 | mBatteryChangedReceiverRegistered = true; 316 | } 317 | mHandler.postDelayed(mTimerTick, 1000); 318 | } 319 | 320 | @Override 321 | public void onPause() { 322 | super.onPause(); 323 | mHandler.removeCallbacks(mTimerTick); 324 | if (mBatteryChangedReceiverRegistered) { 325 | getActivity().unregisterReceiver(this.mBatteryChangedReceiver); 326 | } 327 | } 328 | 329 | private void setClock() { 330 | Date date = new Date(System.currentTimeMillis()); 331 | mClock.setText(mTimeFormat.format(date)); 332 | mDate.setText(mDateFormat.format(date)); 333 | mHandler.postDelayed(mTimerTick, 1000); 334 | } 335 | 336 | @Override 337 | public boolean onLongClick(View v) { 338 | if (v instanceof ApplicationView) { 339 | ApplicationView appView = (ApplicationView) v; 340 | if (appView.hasPackage() && mSetup.iconsLocked()) { 341 | Toast.makeText(getActivity(), R.string.home_locked, Toast.LENGTH_SHORT).show(); 342 | } else { 343 | openApplicationList(ApplicationList.VIEW_LIST, appView.getPosition(), appView.hasPackage(), REQUEST_CODE_APPLICATION_LIST); 344 | } 345 | return (true); 346 | } 347 | return (false); 348 | } 349 | 350 | @Override 351 | public void onClick(View v) { 352 | if (v instanceof ApplicationView) { 353 | openApplication((ApplicationView) v); 354 | return; 355 | } 356 | 357 | switch (v.getId()) { 358 | case R.id.application_grid: { 359 | openApplicationList(ApplicationList.VIEW_GRID, 0, false, REQUEST_CODE_APPLICATION_START); 360 | } 361 | break; 362 | 363 | case R.id.settings: 364 | startActivityForResult(new Intent(getContext(), Preferences.class), REQUEST_CODE_PREFERENCES); 365 | break; 366 | } 367 | 368 | } 369 | 370 | private void openApplication(ApplicationView v) { 371 | if (v.hasPackage() == false) { 372 | openApplicationList(ApplicationList.VIEW_LIST, v.getPosition(), false, REQUEST_CODE_APPLICATION_LIST); 373 | return; 374 | } 375 | 376 | try { 377 | Toast.makeText(getActivity(), v.getName(), Toast.LENGTH_SHORT).show(); 378 | startActivity(getLaunchIntentForPackage(v.getPackageName())); 379 | } catch (Exception e) { 380 | Toast.makeText(getActivity(), v.getName() + " : " + e.getMessage(), Toast.LENGTH_LONG).show(); 381 | } 382 | } 383 | 384 | private void openApplication(String packageName) { 385 | try { 386 | Intent startApp = getLaunchIntentForPackage(packageName); 387 | Toast.makeText(getActivity(), packageName, Toast.LENGTH_SHORT).show(); 388 | startActivity(startApp); 389 | } catch (Exception e) { 390 | Toast.makeText(getActivity(), packageName + " : " + e.getMessage(), Toast.LENGTH_LONG).show(); 391 | } 392 | } 393 | 394 | private void openApplicationList(int viewType, int appNum, boolean showDelete, int requestCode) { 395 | Intent intent = new Intent(getActivity(), ApplicationList.class); 396 | intent.putExtra(ApplicationList.APPLICATION_NUMBER, appNum); 397 | intent.putExtra(ApplicationList.VIEW_TYPE, viewType); 398 | intent.putExtra(ApplicationList.SHOW_DELETE, showDelete); 399 | startActivityForResult(intent, requestCode); 400 | } 401 | 402 | private Intent getLaunchIntentForPackage(String packageName) { 403 | PackageManager pm = getActivity().getPackageManager(); 404 | Intent launchIntent = pm.getLaunchIntentForPackage(packageName); 405 | 406 | if(launchIntent == null) { 407 | launchIntent = pm.getLeanbackLaunchIntentForPackage(packageName); 408 | } 409 | 410 | return launchIntent; 411 | } 412 | 413 | @Override 414 | public void onActivityResult(int requestCode, int resultCode, Intent intent) { 415 | super.onActivityResult(requestCode, resultCode, intent); 416 | switch (requestCode) { 417 | case REQUEST_CODE_WALLPAPER: 418 | break; 419 | case REQUEST_CODE_PREFERENCES: 420 | restartActivity(); 421 | break; 422 | case REQUEST_CODE_APPLICATION_START: 423 | if (intent != null) 424 | openApplication(intent.getExtras().getString(ApplicationList.PACKAGE_NAME)); 425 | break; 426 | case REQUEST_CODE_APPLICATION_LIST: 427 | if (resultCode == Activity.RESULT_OK) { 428 | Bundle extra = intent.getExtras(); 429 | int appNum = intent.getExtras().getInt(ApplicationList.APPLICATION_NUMBER); 430 | 431 | if (extra.containsKey(ApplicationList.DELETE) && extra.getBoolean(ApplicationList.DELETE)) { 432 | writePreferences(appNum, null); 433 | } else { 434 | writePreferences(appNum, 435 | intent.getExtras().getString(ApplicationList.PACKAGE_NAME) 436 | ); 437 | } 438 | updateApplications(); 439 | } 440 | break; 441 | } 442 | } 443 | 444 | 445 | } 446 | --------------------------------------------------------------------------------