diff --git a/app/build.gradle b/app/build.gradle index 8a7ab81..e94bdd8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.application' android { compileSdkVersion 25 - buildToolsVersion "25.0.2" + buildToolsVersion "28.0.3" defaultConfig { applicationId "by.yauhenl.gardine" minSdkVersion 23 @@ -20,13 +20,8 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { - exclude group: 'com.android.support', module: 'support-annotations' - }) - compile 'com.android.support:appcompat-v7:25.2.0' - //compile 'com.android.support.constraint:constraint-layout:1.1.3' - //testCompile 'org.testng:testng:7.1.0' - testCompile 'junit:junit:4.12' - testCompile 'org.assertj:assertj-core:3.16.1' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.android.support:appcompat-v7:25.2.0' + testImplementation 'junit:junit:4.12' + testImplementation 'org.assertj:assertj-core:3.16.1' } diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000..d2e0bc0 Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ diff --git a/app/src/main/java/by/yauhenl/gardine/App.java b/app/src/main/java/by/yauhenl/gardine/App.java index 27d5931..43ea5b3 100644 --- a/app/src/main/java/by/yauhenl/gardine/App.java +++ b/app/src/main/java/by/yauhenl/gardine/App.java @@ -7,33 +7,46 @@ */ public class App { - public final String title; - public final String packageName; - public final Intent startIntent; + final String title; + final String packageName; + final Intent startIntent; - public App(String title, String packageName, Intent startIntent) { + App(String title, String packageName, Intent startIntent) { this.title = title; this.packageName = packageName; this.startIntent = startIntent; } - public String toString() { - return this.title; - } - @Override public boolean equals(Object obj) { if(obj == null) { return false; } - if(!(obj instanceof App)) { - return false; + String pn = ""; + if(obj instanceof String) { + pn = (String) obj; + } else if (obj instanceof App) { + pn = ((App) obj).packageName; } - return this.packageName.equals(((App) obj).packageName); + return this.packageName.equals(pn); } @Override public int hashCode() { return this.packageName.hashCode(); } + + // WARN: it is used in list item view + @Override + public String toString() { + return this.title; + } + + public String toLogString() { + return "App{" + + "title='" + title + '\'' + + ", packageName='" + packageName + '\'' + + ", startIntent=" + startIntent + + '}'; + } } diff --git a/app/src/main/java/by/yauhenl/gardine/DiscardingStack.java b/app/src/main/java/by/yauhenl/gardine/DiscardingStack.java index 08a9b10..8394ece 100644 --- a/app/src/main/java/by/yauhenl/gardine/DiscardingStack.java +++ b/app/src/main/java/by/yauhenl/gardine/DiscardingStack.java @@ -1,7 +1,6 @@ package by.yauhenl.gardine; import java.util.ArrayDeque; -import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; @@ -30,7 +29,7 @@ public void add(T app) { apps.add(app); } - public Collection getAll() { + public ArrayDeque getAll() { ArrayDeque reversed = new ArrayDeque<>(); for (T a : this.apps) { reversed.push(a); diff --git a/app/src/main/java/by/yauhenl/gardine/GardineWidgetService.java b/app/src/main/java/by/yauhenl/gardine/GardineWidgetService.java index ab35adc..7b4c300 100644 --- a/app/src/main/java/by/yauhenl/gardine/GardineWidgetService.java +++ b/app/src/main/java/by/yauhenl/gardine/GardineWidgetService.java @@ -21,7 +21,9 @@ import android.widget.ImageView; import android.widget.ListView; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collection; import static android.widget.AdapterView.INVALID_POSITION; @@ -29,21 +31,24 @@ public class GardineWidgetService extends AccessibilityService { public static final String LOG_TAG_COORD = "coord"; public static final String LOG_TAG_RECENT_APPS = "recent_apps"; + public static final String LOG_TAG_EVENT = "event"; + // TODO: move to preferences private static final int MAX_ITEMS = 6; private static final int SHOW_X_THRESHOLD = 30; private static final int SCROLL_Y_THRESHOLD = 45; private static final int VIBRATE_DURATION = 20; + private static final int MAX_Y_DIFF = MAX_ITEMS * SCROLL_Y_THRESHOLD - SCROLL_Y_THRESHOLD/2; + private static final int MIN_Y_DIFF = 0; private WindowManager windowManager; private View gardine; - private ArrayList recentApps; private DiscardingStack recentActivities; + private String currentAppPackage; private ArrayAdapter recentAppsAdapter; private Vibrator vibrator; public GardineWidgetService() { - this.recentApps = new ArrayList<>(); this.recentActivities = new DiscardingStack<>(MAX_ITEMS); } @@ -66,34 +71,39 @@ protected void onServiceConnected() { @Override public void onAccessibilityEvent(AccessibilityEvent event) { + Log.d(LOG_TAG_EVENT, "Got event: " + event); if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { return; } - if (event.getPackageName() != null && event.getClassName() != null) { - ComponentName componentName = new ComponentName( - event.getPackageName().toString(), - event.getClassName().toString() - ); - - ActivityInfo activityInfo; - PackageManager pm = this.getPackageManager(); - try { - activityInfo = pm.getActivityInfo(componentName, 0); - } catch (PackageManager.NameNotFoundException e) { - Log.i(LOG_TAG_RECENT_APPS, "Ignore window of component: " + componentName); - return; - } - Intent startIntent = pm.getLaunchIntentForPackage(activityInfo.packageName); - if (startIntent == null) { - Log.d(LOG_TAG_RECENT_APPS, "Skipping package " + activityInfo.packageName + " due to absence of launch intent"); - return; - } + if (event.getPackageName() == null || event.getClassName() == null) { + return; + } + this.currentAppPackage = event.getPackageName().toString(); + + ComponentName componentName = new ComponentName( + event.getPackageName().toString(), + event.getClassName().toString() + ); + + ActivityInfo activityInfo; + PackageManager pm = this.getPackageManager(); + try { + activityInfo = pm.getActivityInfo(componentName, 0); + } catch (PackageManager.NameNotFoundException e) { + Log.i(LOG_TAG_RECENT_APPS, "Ignore window of component: " + componentName); + return; + } + Intent startIntent = pm.getLaunchIntentForPackage(activityInfo.packageName); + if (startIntent == null) { + Log.d(LOG_TAG_RECENT_APPS, "Skipping package " + activityInfo.packageName + " due to absence of launch intent"); + return; + } - String label = pm.getApplicationLabel(activityInfo.applicationInfo).toString(); - this.recentActivities.add(new App(label, activityInfo.packageName, startIntent)); + String label = pm.getApplicationLabel(activityInfo.applicationInfo).toString(); + App a = new App(label, activityInfo.packageName, startIntent); + this.recentActivities.add(a); - Log.i(LOG_TAG_RECENT_APPS, componentName.flattenToShortString()); - } + Log.i(LOG_TAG_RECENT_APPS, "Added app to the stack: " + a.toLogString()); } @@ -130,7 +140,7 @@ public void onCreate() { this.recentAppsAdapter = new ArrayAdapter<>( this, R.layout.item, R.id.item, - this.recentApps); + new ArrayList(MAX_ITEMS)); final ListView tasksList = (ListView) gardine.findViewById(R.id.tasks_list); tasksList.setAdapter(this.recentAppsAdapter); @@ -173,7 +183,7 @@ public boolean onTouch(View v, MotionEvent event) { Log.d(LOG_TAG_COORD, "UP at " + event.getRawX() + ", " + event.getRawY()); - if(!isHidden()) { + if (!isHidden()) { int checkedPos = tasksList.getCheckedItemPosition(); if (checkedPos != INVALID_POSITION) { App selectedApp = (App) tasksList.getItemAtPosition(checkedPos); @@ -197,19 +207,31 @@ public boolean onTouch(View v, MotionEvent event) { show(); } } else { - if(event.getRawX() - this.initialShowX > SHOW_X_THRESHOLD/2) { + if (event.getRawX() - this.initialShowX > SHOW_X_THRESHOLD / 2) { hide(); return true; } - int Ydiff = (int) (event.getRawY() - this.initialShowY); + int yd = (int) (event.getRawY() - this.initialShowY); + int oyd = yd; + yd = Math.min(yd, MAX_Y_DIFF); + yd = Math.max(yd, MIN_Y_DIFF); + int correction = oyd - yd; + this.initialShowY += correction; + int itemsNumber = recentAppsAdapter.getCount(); if (itemsNumber > 0) { - int selectedItem = Math.abs((Ydiff / SCROLL_Y_THRESHOLD) % itemsNumber); + int selectedItem = (yd / SCROLL_Y_THRESHOLD); + if(selectedItem < 0) { + selectedItem = 0; + } else if (selectedItem >= itemsNumber) { + selectedItem = itemsNumber - 1; + } + int prevItem = tasksList.getCheckedItemPosition(); if (prevItem != selectedItem) { - Log.d(LOG_TAG_COORD, "Vibrate at Ydiff=" + Ydiff + ", prevItem=" + prevItem + ", curItem=" + selectedItem); + Log.d(LOG_TAG_COORD, "Vibrate at Ydiff=" + yd + ", prevItem=" + prevItem + ", curItem=" + selectedItem); vibrator.vibrate(VIBRATE_DURATION); tasksList.setItemChecked(selectedItem, true); } @@ -231,8 +253,14 @@ public void onDestroy() { } private void actualizeRecentApps() { - this.recentApps.clear(); - this.recentApps.addAll(this.recentActivities.getAll()); + this.recentAppsAdapter.clear(); + + ArrayDeque recentApps = this.recentActivities.getAll(); + if(this.currentAppPackage != null) { + boolean removed = recentApps.removeFirstOccurrence(new App(null, this.currentAppPackage, null)); + Log.d(LOG_TAG_RECENT_APPS, "Current app " + this.currentAppPackage + " has been removed: " + removed); + } + this.recentAppsAdapter.addAll(recentApps); this.recentAppsAdapter.notifyDataSetChanged(); } } diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bc..cc235d7 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0c..2afe458 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0..5539eab 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72c..1a78df1 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index aee44e1..578552d 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/test/java/by/yauhenl/gardine/DiscardingStackTest.java b/app/src/test/java/by/yauhenl/gardine/DiscardingStackTest.java index bcc3fa0..8a28ea3 100644 --- a/app/src/test/java/by/yauhenl/gardine/DiscardingStackTest.java +++ b/app/src/test/java/by/yauhenl/gardine/DiscardingStackTest.java @@ -3,7 +3,7 @@ import org.junit.Test; import static org.assertj.core.api.Java6Assertions.*; -public class RecentActivitiesTest { +public class DiscardingStackTest { private static final String a = "a", b = "b", c = "c", d = "d"; diff --git a/build.gradle b/build.gradle index 1ea4bd0..c5c31db 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,10 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:3.4.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,6 +16,7 @@ buildscript { allprojects { repositories { jcenter() + google() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 58286dc..f8e3c43 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Mar 10 14:43:36 IST 2017 +#Fri Jul 24 21:17:47 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip