diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index a5f05cd..2370474 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -21,5 +21,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 8a1bf36..7143f04 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -20,14 +20,45 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
+ compileOptions {
+ // This library uses Java 8 features, this is required
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+repositories {
+ maven { url 'https://jitpack.io' }
}
-
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.android.support:palette-v7:29.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ compile 'com.crossbowffs.remotepreferences:remotepreferences:0.7'
+
+ def libsuVersion = '3.0.2'
+
+ // The core module is used by all other components
+ implementation "com.github.topjohnwu.libsu:core:${libsuVersion}"
+
+ // Optional: APIs for creating root services
+ // implementation "com.github.topjohnwu.libsu:service:${libsuVersion}"
+
+ // Optional: For com.topjohnwu.superuser.io classes
+ implementation "com.github.topjohnwu.libsu:io:${libsuVersion}"
+
+ // Optional: Bundle prebuilt BusyBox binaries
+ // implementation "com.github.topjohnwu.libsu:busybox:${libsuVersion}"
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1cbfe6d..fda0b11 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,6 +2,7 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/de/valentin/colorconsistency/BootReceiver.java b/app/src/main/java/de/valentin/colorconsistency/BootReceiver.java
new file mode 100644
index 0000000..6482e8c
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/BootReceiver.java
@@ -0,0 +1,25 @@
+package de.valentin.colorconsistency;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import static android.app.AlarmManager.*;
+
+public class BootReceiver extends BroadcastReceiver
+{
+ public void onReceive(Context context, Intent intent)
+ {
+ // execute when Boot Completed
+ /**Schedule your Alarm Here**/
+
+
+ Intent intentn = new Intent(context, ImageChangeService.class);
+ PendingIntent pintent = PendingIntent.getService(context, 0, intentn, 0);
+ AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarm.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), 2*INTERVAL_HOUR, pintent);
+
+ }
+}
diff --git a/app/src/main/java/de/valentin/colorconsistency/ImageChangeService.java b/app/src/main/java/de/valentin/colorconsistency/ImageChangeService.java
index 735276e..3895e09 100644
--- a/app/src/main/java/de/valentin/colorconsistency/ImageChangeService.java
+++ b/app/src/main/java/de/valentin/colorconsistency/ImageChangeService.java
@@ -3,8 +3,12 @@ package de.valentin.colorconsistency;
import android.app.IntentService;
import android.content.Intent;
import androidx.annotation.Nullable;
-import de.valentin.colorconsistency.ToKWLP.CreateKLWPIntent;
-import de.valentin.colorconsistency.image.ImageChooser;
+import de.valentin.colorconsistency.ToGravityBox.GravitBoxConnector;
+import de.valentin.colorconsistency.ToKWLP.KLWPConnector;
+import de.valentin.colorconsistency.ToNova.NovaLauncherConnector;
+import de.valentin.colorconsistency.ToSwiftkey.SwiftkeyConnector;
+import de.valentin.colorconsistency.image.RandomImage;
+import de.valentin.colorconsistency.image.SelectedColors;
import java.io.File;
@@ -18,13 +22,27 @@ public class ImageChangeService extends IntentService {
public ImageChangeService(String name) {
super(name);
}
+ public ImageChangeService() {
+ super("ImageChangeService");
+ }
+
@Override
protected void onHandleIntent(@Nullable Intent intent) {
- ImageChooser imageChooser = new ImageChooser(new File("/storage/emulated/0/Pictures/Wallpapers"));
- String imagePath=imageChooser.getPath();
+ RandomImage randomImage = new RandomImage(new File("/storage/emulated/0/Pictures/Wallpapers"));
+ String imagePath= randomImage.getPath();
+ SelectedColors colors = randomImage.getSelectedColors();
+
+ sendBroadcast(new KLWPConnector(imagePath, colors).getIntent());
+
+ sendBroadcast(new GravitBoxConnector(colors).getIntent());
+
+ SwiftkeyConnector swiftkeyConnector = new SwiftkeyConnector(colors.getForeground());
+ swiftkeyConnector.writeFile();
+ sendBroadcast(swiftkeyConnector.getIntent());
+
+ new NovaLauncherConnector(colors).writeFile();
- sendBroadcast(new CreateKLWPIntent("imagepath",imagePath).getIntent());
}
diff --git a/app/src/main/java/de/valentin/colorconsistency/ImageServiceStarter.java b/app/src/main/java/de/valentin/colorconsistency/ImageServiceStarter.java
new file mode 100644
index 0000000..aed0699
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/ImageServiceStarter.java
@@ -0,0 +1,17 @@
+package de.valentin.colorconsistency;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+
+public class ImageServiceStarter extends Activity {
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = new Intent(this, ImageChangeService.class);
+ startService(intent);
+ finish();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/de/valentin/colorconsistency/MainActivity.java b/app/src/main/java/de/valentin/colorconsistency/MainActivity.java
index 319e2ac..9e66f7d 100644
--- a/app/src/main/java/de/valentin/colorconsistency/MainActivity.java
+++ b/app/src/main/java/de/valentin/colorconsistency/MainActivity.java
@@ -1,14 +1,16 @@
package de.valentin.colorconsistency;
+import android.content.Intent;
+import android.graphics.Bitmap;
import android.view.View;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
-import de.valentin.colorconsistency.ToKWLP.CreateKLWPIntent;
-import de.valentin.colorconsistency.image.ImageChooser;
-import android.widget.Toast;
+import com.topjohnwu.superuser.io.SuFile;
+import com.topjohnwu.superuser.io.SuFileInputStream;
+import com.topjohnwu.superuser.io.SuFileOutputStream;
-import java.io.File;
+import java.io.*;
public class MainActivity extends AppCompatActivity {
@@ -21,14 +23,39 @@ public class MainActivity extends AppCompatActivity {
public void test(View view){
- ImageChooser imageChooser = new ImageChooser(new File("/storage/emulated/0/Pictures/Wallpapers"));
- String imagePath=imageChooser.getPath();
+// RandomImage randomImage = new RandomImage(new File("/storage/emulated/0/Pictures/Wallpapers"));
+// String imagePath= randomImage.getPath();
+//
+// sendBroadcast(new CreateKLWPIntent("imagepath",imagePath).getIntent());
+//
+//
+//
+// Toast toast = Toast.makeText(this, imagePath, Toast.LENGTH_LONG);
+// toast.show();
- sendBroadcast(new CreateKLWPIntent("imagepath",imagePath).getIntent());
+ StringBuilder stringBuilder =new StringBuilder();
+ File logs = SuFile.open("/cache/im.jpg");
+ if (logs.exists()) {
+ try (InputStream in = new SuFileInputStream(logs);
+ OutputStream out = new SuFileOutputStream("/data/data/com.touchtype.swiftkey/files/custom_themes/custom_b7970643-093a-4a22-8620-de83079bb47d/custom/original_background")) {
+
+ int[] cls = {-11751600};
+ Bitmap myBitmap= Bitmap.createBitmap(cls,1,1,Bitmap.Config.ARGB_8888);
+
+ // Bitmap myBitmap = BitmapFactory.decodeStream(in);
+
+ myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+ Toast toast = Toast.makeText(this, stringBuilder, Toast.LENGTH_LONG);
+ toast.show();
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ Intent intent = new Intent(this, ImageServiceStarter.class);
+ startActivity(intent);
- Toast toast = Toast.makeText(this, imagePath, Toast.LENGTH_LONG);
- toast.show();
}
}
\ No newline at end of file
diff --git a/app/src/main/java/de/valentin/colorconsistency/ToGravityBox/GravitBoxConnector.java b/app/src/main/java/de/valentin/colorconsistency/ToGravityBox/GravitBoxConnector.java
new file mode 100644
index 0000000..487dbea
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/ToGravityBox/GravitBoxConnector.java
@@ -0,0 +1,35 @@
+package de.valentin.colorconsistency.ToGravityBox;
+
+import android.content.Intent;
+import android.graphics.Color;
+import android.util.Log;
+import de.valentin.colorconsistency.image.SelectedColors;
+
+
+public class GravitBoxConnector {
+
+ Intent intent;
+
+ final String ACTION_PREF_PIE_CHANGED = "gravitybox.intent.action.PREF_PIE_CHANGED";
+ final String EXTRA_PIE_COLOR_BG = "pieColorBg";
+ final String EXTRA_PIE_COLOR_FG = "pieColorFg";
+ final String EXTRA_PIE_COLOR_OUTLINE = "pieColorOutline";
+ final String EXTRA_PIE_COLOR_SELECTED = "pieColorSelected";
+ final String EXTRA_PIE_COLOR_TEXT = "pieColorText";
+
+
+
+ public GravitBoxConnector(SelectedColors colors){
+ intent = new Intent(ACTION_PREF_PIE_CHANGED);
+ intent.putExtra(EXTRA_PIE_COLOR_BG, colors.getBackground() );
+ intent.putExtra(EXTRA_PIE_COLOR_FG,colors.getForeground());
+ intent.putExtra(EXTRA_PIE_COLOR_TEXT,colors.getBackground());
+ intent.putExtra(EXTRA_PIE_COLOR_SELECTED, colors.getForeground() );
+ intent.putExtra(EXTRA_PIE_COLOR_OUTLINE, colors.getForeground() );
+
+ }
+
+ public Intent getIntent() {
+ return intent;
+ }
+}
diff --git a/app/src/main/java/de/valentin/colorconsistency/ToKWLP/CreateKLWPIntent.java b/app/src/main/java/de/valentin/colorconsistency/ToKWLP/KLWPConnector.java
similarity index 59%
rename from app/src/main/java/de/valentin/colorconsistency/ToKWLP/CreateKLWPIntent.java
rename to app/src/main/java/de/valentin/colorconsistency/ToKWLP/KLWPConnector.java
index 1112725..1c79813 100644
--- a/app/src/main/java/de/valentin/colorconsistency/ToKWLP/CreateKLWPIntent.java
+++ b/app/src/main/java/de/valentin/colorconsistency/ToKWLP/KLWPConnector.java
@@ -1,8 +1,9 @@
package de.valentin.colorconsistency.ToKWLP;
import android.content.Intent;
+import de.valentin.colorconsistency.image.SelectedColors;
-public class CreateKLWPIntent {
+public class KLWPConnector {
Intent intent;
@@ -15,11 +16,16 @@ public class CreateKLWPIntent {
- public CreateKLWPIntent(String var_name,String value){
+ public KLWPConnector(String imagePath, SelectedColors selectedColors){
intent = new Intent(KUSTOM_ACTION);
intent.putExtra(KUSTOM_ACTION_EXT_NAME, "colorconsistency");
- intent.putExtra(KUSTOM_ACTION_VAR_NAME , var_name);
- intent.putExtra(KUSTOM_ACTION_VAR_VALUE, value);
+
+ intent.putExtra(KUSTOM_ACTION_VAR_NAME_ARRAY , new String[]{"imagepath","background","foreground"});
+ intent.putExtra(KUSTOM_ACTION_VAR_VALUE_ARRAY, new String[]{
+ imagePath,
+ String.format("#%06X", (0xFFFFFF & selectedColors.getBackground())),
+ String.format("#%06X", (0xFFFFFF & selectedColors.getForeground()))
+ });
}
public Intent getIntent() {
diff --git a/app/src/main/java/de/valentin/colorconsistency/ToNova/NovaLauncherConnector.java b/app/src/main/java/de/valentin/colorconsistency/ToNova/NovaLauncherConnector.java
new file mode 100644
index 0000000..c9fa92e
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/ToNova/NovaLauncherConnector.java
@@ -0,0 +1,90 @@
+package de.valentin.colorconsistency.ToNova;
+
+import android.text.TextUtils;
+import com.topjohnwu.superuser.Shell;
+import com.topjohnwu.superuser.io.SuFileInputStream;
+import com.topjohnwu.superuser.io.SuFileOutputStream;
+import de.valentin.colorconsistency.image.SelectedColors;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class NovaLauncherConnector {
+ SelectedColors selectedColors;
+ String filePath="data/data/com.teslacoilsw.launcher/shared_prefs/com.teslacoilsw.launcher_preferences.xml";
+
+ public NovaLauncherConnector(SelectedColors selectedColors) {
+ this.selectedColors = selectedColors;
+ }
+
+ public boolean writeFile(){
+
+ try (InputStream in = new SuFileInputStream(filePath)) {
+
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(in);
+
+
+ XPath xPath = XPathFactory.newInstance().newXPath();
+
+ //replaceValueprefs(String key, String newValue, XPath xPath, Document doc)
+ replaceValueprefs("drawer_background_color",String.valueOf(selectedColors.getForeground()),xPath,doc);
+ replaceValueprefs("appsearch_background",String.valueOf(selectedColors.getForeground()),xPath,doc);
+ replaceValueprefs("drawer_label_color",String.valueOf(selectedColors.getBackground()),xPath,doc);
+ replaceValueprefs("drawer_fastscroll_color",String.valueOf(selectedColors.getBackground()),xPath,doc);
+
+
+ // replaceCSVprefs(String key, String newValue, int index, XPath xPath, Document doc)
+ replaceCSVprefs("folder_window_config",String.valueOf(selectedColors.getForeground()),2,xPath,doc);
+ replaceCSVprefs("folder_cellspecs","#"+Integer.toHexString(selectedColors.getBackground()),3,xPath,doc);
+ replaceCSVprefs("folder_icon_config","#"+Integer.toHexString(selectedColors.getForeground()),3,xPath,doc);
+ replaceCSVprefs("drawer_search_style","#"+Integer.toHexString(selectedColors.getBackground()),2,xPath,doc);
+ replaceCSVprefs("drawer_cellspecs","#"+Integer.toHexString(selectedColors.getBackground()),3,xPath,doc);
+
+ Transformer tf = TransformerFactory.newInstance().newTransformer();
+ tf.setOutputProperty(OutputKeys.INDENT, "yes");
+ tf.setOutputProperty(OutputKeys.METHOD, "xml");
+ tf.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
+
+ DOMSource domSource = new DOMSource(doc);
+
+ OutputStream out = new SuFileOutputStream(filePath);
+ tf.transform(domSource, new StreamResult(out));
+
+ Shell.su("am force-stop com.teslacoilsw.launcher").exec();
+ } catch (XPathExpressionException | SAXException | IOException | ParserConfigurationException | TransformerException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ void replaceCSVprefs(String key, String newValue, int index, XPath xPath, Document doc) throws XPathExpressionException {
+ Node node = (Node) xPath.compile("//string[@name='"+key+"']").evaluate(doc, XPathConstants.NODE);
+ String[] textConent=node.getTextContent().split(":");
+ textConent[index]=newValue;
+ node.setTextContent(TextUtils.join(":",textConent));
+ }
+
+
+ void replaceValueprefs(String key, String newValue, XPath xPath, Document doc) throws XPathExpressionException{
+ Node node = (Node) xPath.compile("//int[@name='"+key+"']/@value").evaluate(doc, XPathConstants.NODE);
+ node.setNodeValue(newValue);
+ }
+
+}
diff --git a/app/src/main/java/de/valentin/colorconsistency/ToSwiftkey/SwiftkeyConnector.java b/app/src/main/java/de/valentin/colorconsistency/ToSwiftkey/SwiftkeyConnector.java
new file mode 100644
index 0000000..f203d7a
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/ToSwiftkey/SwiftkeyConnector.java
@@ -0,0 +1,47 @@
+package de.valentin.colorconsistency.ToSwiftkey;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.widget.Toast;
+import com.topjohnwu.superuser.io.SuFileInputStream;
+import com.topjohnwu.superuser.io.SuFileOutputStream;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class SwiftkeyConnector {
+
+ String themeHash = "custom_b7970643-093a-4a22-8620-de83079bb47d";
+ int color;
+
+
+ public SwiftkeyConnector(int color) {
+ this.color = color;
+ }
+
+ public boolean writeFile(){
+ try (OutputStream out = new SuFileOutputStream("/data/data/com.touchtype.swiftkey/files/custom_themes/"+themeHash +"/custom/original_background")) {
+
+ int[] cls = {color};
+ Bitmap myBitmap= Bitmap.createBitmap(cls,1,1,Bitmap.Config.ARGB_8888);
+
+
+ myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+
+ return true;
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public Intent getIntent(){
+ Intent intent = new Intent("com.mayulive.swiftkeyexi.SET_THEME");
+ intent.putExtra("THEME_HASH",themeHash);
+ return intent;
+ }
+
+
+}
diff --git a/app/src/main/java/de/valentin/colorconsistency/image/ImageChooser.java b/app/src/main/java/de/valentin/colorconsistency/image/ImageChooser.java
deleted file mode 100644
index d140285..0000000
--- a/app/src/main/java/de/valentin/colorconsistency/image/ImageChooser.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package de.valentin.colorconsistency.image;
-
-import java.io.File;
-import java.util.Random;
-
-public class ImageChooser {
- File folder;
- File selected;
- String path;
-
-
- public ImageChooser(File folder) {
- this.folder=folder;
- pickRandomFile();
- }
-
-
-
- public void pickRandomFile(){
- try {
- File[] files = folder.listFiles();
- Random rand = new Random();
-
- selected = files[rand.nextInt(files.length)];
- path = selected.getAbsolutePath();
- }
- catch (Exception e ){
- path="error";
- }
-
- }
-
-
- public String getPath() {
- return path;
- }
-}
diff --git a/app/src/main/java/de/valentin/colorconsistency/image/RandomImage.java b/app/src/main/java/de/valentin/colorconsistency/image/RandomImage.java
new file mode 100644
index 0000000..9476384
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/image/RandomImage.java
@@ -0,0 +1,71 @@
+package de.valentin.colorconsistency.image;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import androidx.palette.graphics.Palette;
+
+import java.io.File;
+import java.util.Random;
+
+public class RandomImage {
+ File folder;
+ File selected;
+ String path;
+ Bitmap bitmap;
+ Palette palette;
+ SelectedColors selectedColors;
+
+
+ public RandomImage(File folder) {
+ this.folder=folder;
+ pickRandomFile();
+ }
+
+ public void pickRandomFile(){
+ try {
+ File[] files = folder.listFiles();
+ Random rand = new Random();
+
+ selected = files[rand.nextInt(files.length)];
+ path = selected.getAbsolutePath();
+
+ bitmap = BitmapFactory.decodeFile(path);
+ palette = Palette.from(bitmap).generate();
+ palette.getDarkMutedColor(0);
+ selectedColors = new SelectedColors(
+ palette.getLightMutedColor(Color.WHITE),
+ palette.getDarkMutedColor(Color.BLACK),
+ palette.getDominantColor(Color.WHITE));
+ }
+ catch (Exception e ){
+ path="error";
+ }
+
+ }
+
+
+ public String getPath() {
+ return path;
+ }
+
+ public File getFolder() {
+ return folder;
+ }
+
+ public File getSelected() {
+ return selected;
+ }
+
+ public Bitmap getBitmap() {
+ return bitmap;
+ }
+
+ public Palette getPalette() {
+ return palette;
+ }
+
+ public SelectedColors getSelectedColors() {
+ return selectedColors;
+ }
+}
diff --git a/app/src/main/java/de/valentin/colorconsistency/image/SelectedColors.java b/app/src/main/java/de/valentin/colorconsistency/image/SelectedColors.java
new file mode 100644
index 0000000..61695e1
--- /dev/null
+++ b/app/src/main/java/de/valentin/colorconsistency/image/SelectedColors.java
@@ -0,0 +1,26 @@
+package de.valentin.colorconsistency.image;
+
+public class SelectedColors {
+ int background;
+ int foreground;
+ int text;
+
+ public SelectedColors(int background, int foreground, int text) {
+ this.background = background;
+ this.foreground = foreground;
+ this.text = text;
+ }
+
+
+ public int getBackground() {
+ return background;
+ }
+
+ public int getForeground() {
+ return foreground;
+ }
+
+ public int getText() {
+ return text;
+ }
+}
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index fac9291..5cd6ad5 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -7,4 +7,13 @@
- @color/colorAccent
+
+
\ No newline at end of file