Skip to content

Commit 6ce5bd5

Browse files
authored
Merge pull request #6 from ISO53/testing
added OSS, privacy policy and release automation
2 parents 041a6c3 + 26e8b5d commit 6ce5bd5

6 files changed

Lines changed: 152 additions & 71 deletions

File tree

.github/workflows/release.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Android Release
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up JDK 17
18+
uses: actions/setup-java@v4
19+
with:
20+
distribution: temurin
21+
java-version: 17
22+
23+
- name: Setup Gradle
24+
uses: gradle/actions/setup-gradle@v4
25+
26+
- name: Decode keystore
27+
run: |
28+
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore.jks
29+
30+
- name: Build APK + AAB
31+
run: |
32+
chmod +x gradlew
33+
./gradlew assembleRelease bundleRelease
34+
35+
- name: Extract versionName
36+
id: version
37+
run: |
38+
VERSION=$(grep versionName app/build.gradle.kts | cut -d '"' -f2)
39+
echo "version=$VERSION" >> $GITHUB_OUTPUT
40+
41+
- name: Create GitHub Release
42+
uses: softprops/action-gh-release@v2
43+
with:
44+
tag_name: v${{ steps.version.outputs.version }}
45+
name: v${{ steps.version.outputs.version }}
46+
generate_release_notes: true
47+
files: |
48+
app/build/outputs/apk/release/*.apk
49+
50+
- name: Upload AAB to Play Store (internal)
51+
uses: r0adkll/upload-google-play@v1
52+
with:
53+
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
54+
packageName: io.github.iso53.nothingcompass
55+
releaseFiles: app/build/outputs/bundle/release/*.aab
56+
track: internal
57+
status: completed

PRIVACY_POLICY.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Privacy Policy for Nothing Compass
2+
3+
**Last Updated: February 1, 2026**
4+
5+
Nothing Compass ("the App") is developed as an Open Source project by Yusuf İhsan Şimşek ("the Developer"). This Privacy Policy explains how information is handled within the App.
6+
7+
## 1. No Data Collection
8+
Nothing Compass is designed to respect your privacy. The App does not collect, store, or transmit any personal data or usage information to the Developer or any third party. All calculations and sensor data processing occur entirely on your device.
9+
10+
## 2. Device Sensors and Location Data
11+
- **Sensors**: The App uses your device's sensors (such as the magnetometer and accelerometer) to provide compass and inclinometer functionality. This sensor data is processed locally on your device in real-time.
12+
- **Location**: If you enable "True North" features, the App may request access to your device's location to calculate magnetic declination. This location data is used only for calculation within the App, is processed locally, and is never uploaded, stored, or shared.
13+
14+
## 3. Permissions
15+
- **Vibrate**: Used to provide haptic feedback to enhance the user experience. This can be disabled in the App settings.
16+
17+
## 4. Third-Party Services
18+
The App may use the following third-party services which may collect information used to identify you:
19+
- **Google Play Services**: Used for features such as In-App Reviews. Google's privacy policy applies: [Google Privacy & Terms](https://policies.google.com/privacy)
20+
- **GitHub**: The App's source code is hosted on GitHub. If you visit the project page or report an issue, GitHub's privacy policy applies.
21+
22+
## 5. User-Initiated Communication
23+
If you choose to contact the Developer for feedback, support, or to report an issue (e.g., via email or GitHub), the information you provide—including any diagnostic information generated by the App—will only be used to address your inquiry and improve the App.
24+
25+
## 6. Changes to This Privacy Policy
26+
The Developer may update this Privacy Policy from time to time. Any changes will be posted on this page with an updated "Last Updated" date.
27+
28+
## 7. Contact
29+
If you have any questions or suggestions regarding this Privacy Policy, please contact the Developer at [ihsansimsek5335@gmail.com](mailto:ihsansimsek5335@gmail.com) or via the [GitHub repository](https://github.com/iso53/Nothing-Compass).

app/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
plugins {
22
alias(libs.plugins.android.application)
3+
id("com.google.android.gms.oss-licenses-plugin")
34
}
45

56
android {
@@ -54,4 +55,11 @@ dependencies {
5455
testImplementation(libs.junit)
5556
androidTestImplementation(libs.ext.junit)
5657
androidTestImplementation(libs.espresso.core)
58+
implementation(libs.play.services.oss.licenses)
59+
}
60+
61+
tasks.register("printVersionName") {
62+
doLast {
63+
println(android.defaultConfig.versionName)
64+
}
5765
}

app/src/main/java/io/github/iso53/nothingcompass/OptionsActivity.java

Lines changed: 49 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import android.content.SharedPreferences;
66
import android.net.Uri;
77
import android.os.Bundle;
8-
import android.view.View;
98

109
import androidx.activity.EdgeToEdge;
1110
import androidx.appcompat.app.AppCompatActivity;
@@ -17,6 +16,7 @@
1716
import androidx.recyclerview.widget.LinearLayoutManager;
1817
import androidx.recyclerview.widget.RecyclerView;
1918

19+
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity;
2020
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
2121

2222
import java.util.ArrayList;
@@ -28,10 +28,6 @@
2828

2929
public class OptionsActivity extends AppCompatActivity {
3030

31-
private static void onClick(View v) {
32-
// TODO: Show OSS licenses
33-
}
34-
3531
@Override
3632
protected void onCreate(Bundle savedInstanceState) {
3733
// Apply theme before super.onCreate
@@ -46,14 +42,14 @@ protected void onCreate(Bundle savedInstanceState) {
4642

4743
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.optionsToolbar),
4844
(v, insets) -> {
49-
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
50-
v.setPadding(systemBars.left, systemBars.top, systemBars.right, 0);
51-
return insets;
52-
});
45+
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
46+
v.setPadding(systemBars.left, systemBars.top, systemBars.right, 0);
47+
return insets;
48+
});
5349

5450
// Handle bottom padding for RecyclerView to avoid navigation bar overlap
5551
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.optionsRecyclerView), (v,
56-
insets) -> {
52+
insets) -> {
5753
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
5854
v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(),
5955
systemBars.bottom + v.getPaddingBottom());
@@ -62,8 +58,7 @@ protected void onCreate(Bundle savedInstanceState) {
6258

6359
// Setup Toolbar
6460
findViewById(R.id.optionsToolbar).setOnClickListener(v -> finish());
65-
((androidx.appcompat.widget.Toolbar) findViewById(R.id.optionsToolbar))
66-
.setNavigationOnClickListener(v -> finish());
61+
((androidx.appcompat.widget.Toolbar) findViewById(R.id.optionsToolbar)).setNavigationOnClickListener(v -> finish());
6762

6863
setupRecyclerView();
6964
}
@@ -78,8 +73,8 @@ private void setupRecyclerView() {
7873
items.add(new OptionItem(getString(R.string.category_preferences)));
7974
items.add(new OptionItem(getString(R.string.item_theme), null, R.drawable.ic_settings,
8075
v -> showThemeSelectionDialog()));
81-
items.add(new OptionItem(getString(R.string.item_haptic_feedback), null, R.drawable.ic_vibration,
82-
v -> showHapticFeedbackSelectionDialog()));
76+
items.add(new OptionItem(getString(R.string.item_haptic_feedback), null,
77+
R.drawable.ic_vibration, v -> showHapticFeedbackSelectionDialog()));
8378

8479
// Category: App
8580
items.add(new OptionItem(getString(R.string.category_app)));
@@ -95,13 +90,14 @@ private void setupRecyclerView() {
9590
// Category: Support
9691
items.add(new OptionItem(getString(R.string.category_support)));
9792
items.add(new OptionItem(getString(R.string.item_license), null, R.drawable.ic_license,
98-
v -> openUrl("https://github.com/iso53/Nothing-Compass/blob/main/LICENSE.md")));
93+
v -> openUrl("https://github.com/iso53/Nothing-Compass/blob/main/LICENSE")));
9994
items.add(new OptionItem(getString(R.string.item_third_party_licenses), null,
100-
R.drawable.ic_verified, OptionsActivity::onClick));
95+
R.drawable.ic_verified, v -> startActivity(new Intent(this,
96+
OssLicensesMenuActivity.class))));
10197
items.add(new OptionItem(getString(R.string.item_manage_permission), null,
10298
R.drawable.ic_permission, v -> openAppSettings()));
103-
items.add(new OptionItem(getString(R.string.item_help_feedback), null, R.drawable.ic_help,
104-
v -> sendFeedbackEmail()));
99+
items.add(new OptionItem(getString(R.string.item_help_feedback), null, R.drawable.ic_help
100+
, v -> sendFeedbackEmail()));
105101
items.add(new OptionItem(getString(R.string.item_rate_app), null, R.drawable.ic_rate,
106102
v -> openPlayStore()));
107103

@@ -110,63 +106,50 @@ private void setupRecyclerView() {
110106
}
111107

112108
private void showThemeSelectionDialog() {
113-
String[] themes = {
114-
getString(R.string.theme_light),
115-
getString(R.string.theme_dark),
116-
getString(R.string.theme_system)
117-
};
109+
String[] themes = {getString(R.string.theme_light), getString(R.string.theme_dark),
110+
getString(R.string.theme_system)};
118111

119112
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
120113
int currentTheme = prefs.getInt(PreferenceConstants.THEME,
121114
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
122115

123116
int checkedItem = 2; // Default to System
124-
if (currentTheme == AppCompatDelegate.MODE_NIGHT_NO)
125-
checkedItem = 0;
126-
else if (currentTheme == AppCompatDelegate.MODE_NIGHT_YES)
127-
checkedItem = 1;
128-
129-
new MaterialAlertDialogBuilder(this)
130-
.setTitle(R.string.item_theme)
131-
.setSingleChoiceItems(themes, checkedItem, (dialog, which) -> {
132-
int mode;
133-
switch (which) {
134-
case 0:
135-
mode = AppCompatDelegate.MODE_NIGHT_NO;
136-
break;
137-
case 1:
138-
mode = AppCompatDelegate.MODE_NIGHT_YES;
139-
break;
140-
default:
141-
mode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
142-
break;
143-
}
144-
prefs.edit().putInt(PreferenceConstants.THEME, mode).apply();
145-
AppCompatDelegate.setDefaultNightMode(mode);
146-
dialog.dismiss();
147-
})
148-
.show();
117+
if (currentTheme == AppCompatDelegate.MODE_NIGHT_NO) checkedItem = 0;
118+
else if (currentTheme == AppCompatDelegate.MODE_NIGHT_YES) checkedItem = 1;
119+
120+
new MaterialAlertDialogBuilder(this).setTitle(R.string.item_theme).setSingleChoiceItems(themes, checkedItem, (dialog, which) -> {
121+
int mode;
122+
switch (which) {
123+
case 0:
124+
mode = AppCompatDelegate.MODE_NIGHT_NO;
125+
break;
126+
case 1:
127+
mode = AppCompatDelegate.MODE_NIGHT_YES;
128+
break;
129+
default:
130+
mode = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
131+
break;
132+
}
133+
prefs.edit().putInt(PreferenceConstants.THEME, mode).apply();
134+
AppCompatDelegate.setDefaultNightMode(mode);
135+
dialog.dismiss();
136+
}).show();
149137
}
150138

151139
private void showHapticFeedbackSelectionDialog() {
152-
String[] options = {
153-
getString(R.string.haptic_feedback_on),
154-
getString(R.string.haptic_feedback_off)
155-
};
140+
String[] options = {getString(R.string.haptic_feedback_on),
141+
getString(R.string.haptic_feedback_off)};
156142

157143
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
158144
boolean currentHaptic = prefs.getBoolean(PreferenceConstants.HAPTIC_FEEDBACK, true);
159145

160146
int checkedItem = currentHaptic ? 0 : 1;
161147

162-
new MaterialAlertDialogBuilder(this)
163-
.setTitle(R.string.item_haptic_feedback)
164-
.setSingleChoiceItems(options, checkedItem, (dialog, which) -> {
165-
boolean enabled = (which == 0);
166-
prefs.edit().putBoolean(PreferenceConstants.HAPTIC_FEEDBACK, enabled).apply();
167-
dialog.dismiss();
168-
})
169-
.show();
148+
new MaterialAlertDialogBuilder(this).setTitle(R.string.item_haptic_feedback).setSingleChoiceItems(options, checkedItem, (dialog, which) -> {
149+
boolean enabled = (which == 0);
150+
prefs.edit().putBoolean(PreferenceConstants.HAPTIC_FEEDBACK, enabled).apply();
151+
dialog.dismiss();
152+
}).show();
170153
}
171154

172155
private void openPlayStore() {
@@ -175,8 +158,8 @@ private void openPlayStore() {
175158
startActivity(new Intent(Intent.ACTION_VIEW,
176159
Uri.parse("market://details?id=" + packageName)));
177160
} catch (ActivityNotFoundException e) {
178-
startActivity(new Intent(Intent.ACTION_VIEW,
179-
Uri.parse("https://play.google.com/store/apps/details?id=" + packageName)));
161+
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google" +
162+
".com/store/apps/details?id=" + packageName)));
180163
}
181164
}
182165

@@ -199,18 +182,13 @@ private void sendFeedbackEmail() {
199182
} catch (Exception ignored) {
200183
}
201184

202-
String deviceInfo = "\n\n\n------------------------------" +
203-
"\nDevice Diagnostics (Please do not delete):" +
204-
"\nApp Version: " + appVersion +
205-
"\nAndroid Version: " + android.os.Build.VERSION.RELEASE + " (SDK "
206-
+ android.os.Build.VERSION.SDK_INT + ")" +
207-
"\nManufacturer: " + android.os.Build.MANUFACTURER +
208-
"\nModel: " + android.os.Build.MODEL +
209-
"\nProduct: " + android.os.Build.PRODUCT;
185+
String deviceInfo = "\n\n\n------------------------------" + "\nDevice Diagnostics " +
186+
"(Please do not delete):" + "\nApp Version: " + appVersion + "\nAndroid Version: "
187+
+ android.os.Build.VERSION.RELEASE + " (SDK " + android.os.Build.VERSION.SDK_INT + ")" + "\nManufacturer: " + android.os.Build.MANUFACTURER + "\nModel: " + android.os.Build.MODEL + "\nProduct: " + android.os.Build.PRODUCT;
210188

211189
Intent intent = new Intent(Intent.ACTION_SENDTO);
212190
intent.setData(Uri.parse("mailto:"));
213-
intent.putExtra(Intent.EXTRA_EMAIL, new String[] { feedbackEmail });
191+
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{feedbackEmail});
214192
intent.putExtra(Intent.EXTRA_SUBJECT, "Feedback/Support - Nothing Compass");
215193
intent.putExtra(Intent.EXTRA_TEXT, deviceInfo);
216194

gradle/libs.versions.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ material = "1.13.0"
88
activity = "1.12.2"
99
constraintlayout = "2.2.1"
1010
fragment = "1.8.9"
11+
playServicesOssLicenses = "17.3.0"
1112
sdpAndroid = "1.1.1"
1213
sspAndroid = "1.1.1"
1314
viewpager2 = "1.0.0"
@@ -23,6 +24,7 @@ material = { group = "com.google.android.material", name = "material", version.r
2324
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
2425
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
2526
fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" }
27+
play-services-oss-licenses = { module = "com.google.android.gms:play-services-oss-licenses", version.ref = "playServicesOssLicenses" }
2628
sdp-android = { module = "com.intuit.sdp:sdp-android", version.ref = "sdpAndroid" }
2729
ssp-android = { module = "com.intuit.ssp:ssp-android", version.ref = "sspAndroid" }
2830
viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref = "viewpager2" }

settings.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ pluginManagement {
99
}
1010
mavenCentral()
1111
gradlePluginPortal()
12+
resolutionStrategy {
13+
eachPlugin {
14+
if (requested.id.id == "com.google.android.gms.oss-licenses-plugin") {
15+
useModule("com.google.android.gms:oss-licenses-plugin:0.10.10")
16+
}
17+
}
18+
}
1219
}
1320
}
1421
dependencyResolutionManagement {

0 commit comments

Comments
 (0)