diff --git a/app/build.gradle b/app/build.gradle index 4fca161..f6993d2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -43,7 +43,7 @@ dependencies { // Glide implementation 'com.github.bumptech.glide:glide:4.16.0' - implementation 'androidx.preference:preference:1.2.0'// Check for the latest version + implementation 'androidx.preference:preference:1.2.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' //CircleImageView diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index df1d6c6..dd359a6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + @@ -9,6 +13,7 @@ + - + tools:targetApi="33"> + android:value="AIzaSyB7C4QCJEBvS7mFa_DeIZdzqe2hddtl-vk" /> + + + + android:exported="true" + android:screenOrientation="portrait" + tools:ignore="LockedOrientationActivity"> - + + mFlashlightHelper.toggleFlashlight()); + + mFlashlightLevelShifter.setOnProgressChangedListener(progress + -> mFlashlightHelper.setFlashlightBrightness(progress)); + + mFlashlightHelper.getStatusObservable() + .subscribe(this::onFlashlightStatusChanged, this::handleSubscriptionError); + } + + private void initButtons() { + final MaterialButton minimumBrightness = findViewById(R.id.btn_flashlight_lowest); + minimumBrightness.setOnClickListener(v -> { + mFlashlightLevelShifter.setProgress(1); + mFlashlightHelper.setFlashlightBrightness(1); + }); + final MaterialButton mediumBrightness = findViewById(R.id.btn_flashlight_medium); + mediumBrightness.setOnClickListener(v -> { + final int maxBrightness = mFlashlightHelper.getMaxBrightness(); + mFlashlightLevelShifter.setProgress(maxBrightness/2); + mFlashlightHelper.setFlashlightBrightness(maxBrightness/2); + }); + final MaterialButton maximumBrightness = findViewById(R.id.btn_flashlight_maximum); + maximumBrightness.setOnClickListener(v -> { + final int maxBrightness = mFlashlightHelper.getMaxBrightness(); + mFlashlightLevelShifter.setProgress(maxBrightness); + mFlashlightHelper.setFlashlightBrightness(maxBrightness); + }); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mFlashlightHelper.destroy(); + } + + /** + * Apply new ImageResource and color to the {@link #mLightOnIndicator}. + * + * @param status true if flashlight on, false otherwise. + */ + private void onFlashlightStatusChanged(final boolean status) { + if (status) { + mLightOnIndicator.setImageResource(R.drawable.ic_flashlight_on); + mLightOnIndicator.setColorFilter(getColor( + R.color.md_theme_primary_highContrast), PorterDuff.Mode.SRC_IN); + } else { + mLightOnIndicator.setImageResource(R.drawable.ic_flashlight_off); + mLightOnIndicator.setColorFilter(getColor( + R.color.md_theme_outline), PorterDuff.Mode.SRC_IN); + } + } + + /** + * Helper method to print any potential errors while subscribing to the current status observable. + * + * @param throwable The error that has been thrown. + */ + private void handleSubscriptionError(final Throwable throwable) { + Log.e(TAG, "handleSubscriptionError: Error while listening to flashlight status", throwable); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java index 5174f5b..0850366 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java @@ -1,5 +1,6 @@ package com.aldo.apps.familyhelpers; +import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.POST_NOTIFICATIONS; import static com.aldo.apps.familyhelpers.utils.GlobalConstants.SIGN_IN_PROVIDERS; @@ -50,6 +51,11 @@ public class HelperGridActivity extends AppCompatActivity { */ private HelperGroupTile mShareLocationTile; + /** + * {@link HelperGroupTile} holding the flashlight. + */ + private HelperGroupTile mFlashlightTile; + /** * {@link HelperGroupTile} holding the settings. */ @@ -115,6 +121,7 @@ public class HelperGridActivity extends AppCompatActivity { mDevicePolicyHelper = DevicePolicyManagerHelper.getInstance(this); initSleepTimer(); initShareLocation(); + initFlashlight(); initSettings(); } @@ -173,6 +180,23 @@ public class HelperGridActivity extends AppCompatActivity { mShareLocationTile.setTitle(R.string.title_share_location); } + /** + * Helper method to initialize the settings tile. + */ + private void initFlashlight() { + mFlashlightTile = new HelperGroupTile(findViewById(R.id.tile_flashlight)) { + @Override + public void launchHelper() { + if (requestCameraPermission()) { + final Intent intent = new Intent(HelperGridActivity.this, FlashlightActivity.class); + startActivity(intent); + } + } + }; + mFlashlightTile.setTitle(R.string.title_flashlight); + mFlashlightTile.setLogo(R.drawable.ic_flashlight); + } + /** * Helper method to initialize the settings tile. */ @@ -208,6 +232,27 @@ public class HelperGridActivity extends AppCompatActivity { } } + /** + * Helper method to request the CameraPermission as it is required to access the + * flashlight settings. + * + * @return true if the permission is granted, false otherwise. + */ + private boolean requestCameraPermission() { + if (ContextCompat.checkSelfPermission(this, CAMERA) == + PackageManager.PERMISSION_GRANTED) { + // Permission already granted + return true; + } else { + if (shouldShowRequestPermissionRationale(CAMERA)) { + Toast.makeText(HelperGridActivity.this, R.string.flashlight_camera_rationale, + Toast.LENGTH_LONG).show(); + } + mRequestPermissionLauncher.launch(CAMERA); + return false; + } + } + /** * Handle the result of the sign in flow. * diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/ui/FlashLightLevelShifter.java b/app/src/main/java/com/aldo/apps/familyhelpers/ui/FlashLightLevelShifter.java new file mode 100644 index 0000000..1e43cab --- /dev/null +++ b/app/src/main/java/com/aldo/apps/familyhelpers/ui/FlashLightLevelShifter.java @@ -0,0 +1,317 @@ +package com.aldo.apps.familyhelpers.ui; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.MotionEvent; +import android.view.View; + +import com.aldo.apps.familyhelpers.R; + +/** + * A custom SeekBar-like view for displaying and selecting a discrete level, + * such as a flashlight brightness level. The view is composed of vertically + * stacked segments, where the number of segments is determined by the + * {@code maxProgress} property. + * + * NOTE: This class was mainly generated and documented by Google-AI. + */ +public class FlashLightLevelShifter extends View { + + /** + * The paint used to draw the level segments. + */ + private final Paint mLevelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + /** + * The rectangle used to define the boundaries of each level segment. + */ + private final RectF mLevelRect = new RectF(); + + /** + * The width of the canvas. + */ + private int mCanvasWidth = 0; + + /** + * The height of the canvas. + */ + private int mCanvasHeight = 0; + + /** + * The bitmap used to cache the progress drawing. + */ + private Bitmap mProgressBitmap; + + /** + * Listener interface for progress change events. + */ + private SeekBarChangeListener mOnProgressChangedListener; + + /** + * The maximum progress value. This determines the number of segments. + */ + private int mMaxProgress = 1; + + /** + * The current progress value. + */ + private int mProgress = 1; + + /** + * The background color of the level segments. + */ + private int mLevelBackgroundColor = Color.LTGRAY; + + /** + * The corner radius of the level segments. + */ + private float mLevelCornerRadius; + + /** + * The gap between level segments. + */ + private float mLevelGap; + + /** + * The color of the filled level segments (representing progress). + */ + private int mLevelProgressColor = Color.WHITE; + + /** + * Constructor for FlashLightLevelShifter. + * + * @param context The context. + */ + public FlashLightLevelShifter(final Context context) { + this(context, null); + } + + /** + * Constructor for FlashLightLevelShifter. + * + * @param context The context. + * @param attrs The attribute set. + */ + public FlashLightLevelShifter(final Context context, final AttributeSet attrs) { + this(context, attrs, 0); + } + + /** + * Constructor for FlashLightLevelShifter. + * + * @param context The context. + * @param attrs The attribute set. + * @param defStyleAttr The default style attribute. + */ + public FlashLightLevelShifter(final Context context, final AttributeSet attrs, final int defStyleAttr) { + super(context, attrs, defStyleAttr); + + mLevelCornerRadius = calculateDp(4); + mLevelGap = calculateDp(2); + + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.FlashLightLevelShifter); + mLevelBackgroundColor = typedArray.getColor( + R.styleable.FlashLightLevelShifter_level_background_color, + mLevelBackgroundColor + ); + mLevelCornerRadius = typedArray.getDimension( + R.styleable.FlashLightLevelShifter_level_corner_radius, + mLevelCornerRadius + ); + mLevelGap = typedArray.getDimension(R.styleable.FlashLightLevelShifter_level_gap, mLevelGap); + mLevelProgressColor = typedArray.getColor( + R.styleable.FlashLightLevelShifter_level_progress_color, + mLevelProgressColor + ); + typedArray.recycle(); + } + + /** + * Gets the maximum progress value. + * + * @return The maximum progress value. + */ + public int getMaxProgress() { + return mMaxProgress; + } + + /** + * Sets the maximum progress value. + * + * @param maxProgress The maximum progress value. + */ + public void setMaxProgress(final int maxProgress) { + this.mMaxProgress = maxProgress; + this.mProgress = maxProgress; + invalidate(); + } + + /** + * Gets the current progress value. + * + * @return The current progress value. + */ + public int getProgress() { + return mProgress; + } + + /** + * Sets the current progress value. + * + * @param progress The current progress value. + */ + public void setProgress(final int progress) { + setProgressInternal(progress); + } + + @Override + protected void onSizeChanged(final int w, final int h, final int oldw, final int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + mCanvasWidth = w; + mCanvasHeight = h; + mProgressBitmap = Bitmap.createBitmap(getAvailableWidth(), getAvailableHeight(), Bitmap.Config.ARGB_8888); + } + + @Override + protected void onDraw(final Canvas canvas) { + canvas.clipRect( + getPaddingLeft(), + getPaddingTop(), + mCanvasWidth, + mCanvasHeight + ); + int availableWidth = getAvailableWidth(); + int availableHeight = getAvailableHeight(); + float totalLevelHeight = (float) availableHeight / mMaxProgress; + float levelHeight = totalLevelHeight - mLevelGap; + + float previousLevelBottom = getPaddingTop(); + + for (int i = 0; i < mMaxProgress; i++) { + mLevelRect.set( + getPaddingLeft(), + previousLevelBottom, + availableWidth + getPaddingRight(), + previousLevelBottom + levelHeight + ); + if (i + 1 <= mProgress) { + mLevelPaint.setColor(mLevelBackgroundColor); + } else { + mLevelPaint.setColor(mLevelProgressColor); + } + canvas.drawRoundRect(mLevelRect, mLevelCornerRadius, mLevelCornerRadius, mLevelPaint); + previousLevelBottom = mLevelRect.bottom + mLevelGap; + } + } + + /** + * Sets the listener for progress change events. + * + * @param changedListener The listener to set. + */ + public void setOnProgressChangedListener(final SeekBarChangeListener changedListener) { + mOnProgressChangedListener = changedListener; + } + + /** + * + * @param event + */ + private void updateProgress(final MotionEvent event) { + if (getRawProgress(event) != mProgress) { + mProgress = getRawProgress(event); + if (mOnProgressChangedListener != null) { + mOnProgressChangedListener.onProgressChanged(mMaxProgress - mProgress); + } + invalidate(); + } + } + + /** + * Gets the raw progress value from a MotionEvent. + * + * @param event The MotionEvent. + * @return The raw progress value. + */ + private int getRawProgress(final MotionEvent event) { + return Math.round(mMaxProgress * event.getY() / getAvailableHeight()); + } + + /** + * Sets the progress internally and triggers a redraw. + * + * @param progress The new progress value. + */ + protected void setProgressInternal(final int progress) { + this.mProgress = mMaxProgress - progress; + invalidate(); + } + + /** + * Gets the available width for drawing the level segments. + * + * @return The available width. + */ + private int getAvailableWidth() { + int width = mCanvasWidth - getPaddingLeft() - getPaddingRight(); + if (width <= 0) width = 1; + return width; + } + + /** + * Gets the available height for drawing the level segments. + * + * @return The available height. + */ + private int getAvailableHeight() { + int height = mCanvasHeight - getPaddingTop() - getPaddingBottom(); + if (height <= 0) height = 1; + return height; + } + + @Override + public boolean onTouchEvent(final MotionEvent event) { + if (!isEnabled()) return false; + + if (event != null) { + updateProgress(event); + performClick(); + } + return true; + } + + @Override + public boolean performClick() { + super.performClick(); + return true; + } + + private float calculateDp(final int dp) { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + dp, + getResources().getDisplayMetrics() + ); + } + + /** + * Interface definition for a callback to be invoked when the progress + * level is changed. + */ + public interface SeekBarChangeListener { + + /** + * Called when the progress level is changed. + * + * @param progress The new progress level. + */ + void onProgressChanged(final int progress); + } +} diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/utils/FlashlightHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/utils/FlashlightHelper.java new file mode 100644 index 0000000..d979a4d --- /dev/null +++ b/app/src/main/java/com/aldo/apps/familyhelpers/utils/FlashlightHelper.java @@ -0,0 +1,202 @@ +package com.aldo.apps.familyhelpers.utils; + +import android.content.Context; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.os.Build; +import android.text.TextUtils; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import io.reactivex.rxjava3.subjects.BehaviorSubject; + +/** + * Helper class to encapsulate all flashlight related logic. + */ +public class FlashlightHelper { + + /** + * Tag for debugging purposes. + */ + private static final String TAG = "FlashlightHelper"; + + /** + * {@link BehaviorSubject} holding the current status of the flashlight. + */ + private final BehaviorSubject mFlashlightStatusObservable = BehaviorSubject.createDefault(false); + + /** + * The {@link CameraManager} to get access to the flashlight. + */ + private final CameraManager mCameraManager; + + /** + * Unique identifier of the camera that has a flashlight. + */ + private String mCameraId; + + /** + * Max available brightness level. + */ + private Integer mMaxBrightness; + + /** + * The currently selected brightness level (defaults to MAX) + */ + private Integer mCurrentBrightness; + + /** + * The {@link CameraManager.TorchCallback} to be invoked when the flashlight status or availability + * changes. + */ + private final CameraManager.TorchCallback mTorchCallback = new CameraManager.TorchCallback() { + @Override + public void onTorchModeUnavailable(@NonNull final String cameraId) { + super.onTorchModeUnavailable(cameraId); + if (TextUtils.equals(mCameraId, cameraId)) { + Log.d(TAG, "onTorchModeUnavailable: Flashlight became unavailable, propagate off"); + mFlashlightStatusObservable.onNext(false); + } + } + + @Override + public void onTorchModeChanged(@NonNull final String cameraId, final boolean enabled) { + super.onTorchModeChanged(cameraId, enabled); + if (TextUtils.equals(mCameraId, cameraId)) { + Log.d(TAG, "onTorchModeChanged: Flashlight Status changed to [" + enabled + "]"); + mFlashlightStatusObservable.onNext(enabled); + } + } + }; + + /** + * C'Tor. + * + * @param context The {@link Context} from where this was instantiated. + */ + @RequiresApi(api = Build.VERSION_CODES.TIRAMISU) + public FlashlightHelper(final Context context) { + mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); + mCameraManager.registerTorchCallback(mTorchCallback, null); + try { + for (final String camId : mCameraManager.getCameraIdList()) { + final CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(camId); + if (Boolean.TRUE.equals(characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE))) { + mCameraId = camId; + mMaxBrightness = characteristics.get(CameraCharacteristics.FLASH_INFO_STRENGTH_MAXIMUM_LEVEL); + mCurrentBrightness = characteristics.get(CameraCharacteristics.FLASH_INFO_STRENGTH_MAXIMUM_LEVEL); + Log.d(TAG, "maxAvailable = [" + mMaxBrightness + "], current = [" + mCurrentBrightness + "]"); + break; + } + } + } catch (final CameraAccessException e) { + Log.e(TAG, "onCreate: ", e); + } + } + + /** + * Returns the {@link BehaviorSubject} holding the current status of the flashlight. + * + * @return The {@link BehaviorSubject} holding the current status of the flashlight. + */ + public BehaviorSubject getStatusObservable() { + return mFlashlightStatusObservable; + } + + /** + * Returns the max available brightness. + * + * @return The max available brightness. + */ + public int getMaxBrightness() { + return mMaxBrightness == null ? 1 : mMaxBrightness; + } + + /** + * Returns the currently set brightness. + * + * @return The currently set brightness. + */ + public int getCurrentBrightness() { + return mCurrentBrightness; + } + + /** + * Helper method to toggle the flashlight to on/off based on the current state. + */ + public void toggleFlashlight() { + if (mFlashlightStatusObservable.getValue()) { + Log.d(TAG, "toggleFlashlight: Turning off flashlight"); + turnFlashlightOff(); + } else { + Log.d(TAG, "toggleFlashlight: Turning on flashlight"); + turnFlashlightOn(); + } + } + + /** + * Sets the provided brightness level if applicable to the flashlight and turns it on. + * + * @param brightnessLevel The brightness level to be set. + */ + @RequiresApi(api = Build.VERSION_CODES.TIRAMISU) + public void setFlashlightBrightness(final int brightnessLevel) { + if (mCameraId == null) { + Log.e("setFlashlightBrightness", "No camera with flash found."); + return; + } + + if (brightnessLevel > mMaxBrightness || brightnessLevel < 1) { + Log.d(TAG, "setFlashlightBrightness: Invalid brightness setting [" + brightnessLevel + "], ignore..."); + return; + } + + try { + mCameraManager.turnOnTorchWithStrengthLevel(mCameraId, brightnessLevel); + } catch (final CameraAccessException e) { + Log.e("setFlashlightBrightness", "Error setting torch brightness: " + e.getMessage()); + } + } + + /** + * Turn on the flashlight without changing the brightness level. + */ + public void turnFlashlightOn() { + if (mCameraId == null) { + Log.e("turnFlashlightOn", "No camera with flash found."); + return; + } + + try{ + mCameraManager.setTorchMode(mCameraId, true); + }catch (final CameraAccessException e){ + Log.e("turnFlashlightOn", "Error turning torch on: " + e.getMessage()); + } + } + + /** + * Turns off the flashlight. + */ + public void turnFlashlightOff() { + if (mCameraId == null) { + Log.e("turnFlashlightOff", "No camera with flash found."); + return; + } + + try{ + mCameraManager.setTorchMode(mCameraId, false); + }catch(final CameraAccessException e){ + Log.e("turnFlashlightOff", "Error turning torch off: " + e.getMessage()); + } + } + + /** + * Clean up helper method. + */ + public void destroy() { + mCameraManager.unregisterTorchCallback(mTorchCallback); + } +} diff --git a/app/src/main/res/drawable/ic_flashlight.xml b/app/src/main/res/drawable/ic_flashlight.xml new file mode 100644 index 0000000..f7e7076 --- /dev/null +++ b/app/src/main/res/drawable/ic_flashlight.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_flashlight_off.xml b/app/src/main/res/drawable/ic_flashlight_off.xml new file mode 100644 index 0000000..5016c17 --- /dev/null +++ b/app/src/main/res/drawable/ic_flashlight_off.xml @@ -0,0 +1,36 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_flashlight_on.xml b/app/src/main/res/drawable/ic_flashlight_on.xml new file mode 100644 index 0000000..0260cbb --- /dev/null +++ b/app/src/main/res/drawable/ic_flashlight_on.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/active_share_view_holder.xml b/app/src/main/res/layout/active_share_view_holder.xml index 0558b1c..dc3683a 100644 --- a/app/src/main/res/layout/active_share_view_holder.xml +++ b/app/src/main/res/layout/active_share_view_holder.xml @@ -1,16 +1,16 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fb59a6b..d3482a9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -25,21 +25,28 @@ + + diff --git a/app/src/main/res/layout/activity_share_location.xml b/app/src/main/res/layout/activity_share_location.xml index ec779cb..a0d44ca 100644 --- a/app/src/main/res/layout/activity_share_location.xml +++ b/app/src/main/res/layout/activity_share_location.xml @@ -9,7 +9,7 @@ - diff --git a/app/src/main/res/layout/helper_group_tile_item.xml b/app/src/main/res/layout/helper_group_tile_item.xml index d22048c..327a25d 100644 --- a/app/src/main/res/layout/helper_group_tile_item.xml +++ b/app/src/main/res/layout/helper_group_tile_item.xml @@ -12,8 +12,8 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml new file mode 100644 index 0000000..1387032 --- /dev/null +++ b/app/src/main/res/values-de/strings.xml @@ -0,0 +1,41 @@ + + + Doerflinger-Helpers + Diese Aktion benötigt spezielle Geräte-Administratoren Rechte, bitte ertile diese bevor du weitermachts + Wilkommen %s + Wilkommen Test Nutzer + Unbekannter Nutzer + Sleep Timer + Bietet die Möglichkeit, einen Sleep Timer zu setzen, nach dem das Display ausgeschalten wird + Geräte-Administratoren Rechte werden benötigt um den Bildschirm nach einem Timeout auszuschalten + Für diese Funktion wird die Berechtigung eine Benachrichtigung anzuzeigen benötigt + Sleep-Timer läuft. Das Gerät wird in %s gesperrt + Abbrechen + Standort teilen + Du teilst deinen Standort gerade mit der Familie. + Aktuell teilt niemand seinen Standort + Du folgst gerade niemandem + Du folgst %s + Letzter bekannter Standort = %f;%f + Letze bekannte Höhe = %.2f Meter + Letzte bekannte Geschwindigkeit: %.2f m/s (%.1f km/h) + Dieses Update wurde empfangen: %s + Teile deinen Standort + Um deinen Standort auch zu teilen, während die App im Hintergrund ist, bitte wähle die \"Immer\" Option. + Einstellungen + Taschenlampe + Um auf die Taschenlampe zugreifen zu können, wird die Kamera Berechtigung gebraucht. + Aktiviere die Taschenlampe auf minimaler Helligkeit + Aktiviere die Taschenlampe auf mittlerer Helligkeit + Aktiviere die Taschenlampe auf maximaler Helligkeit + Einstellungen + Standort teilen + Standort Update Frequenz + Die Minimal-Zeit in Sekunden zwischen zwei Standort updates. Achtung: Je niedriger dieser Wert, desto höher die Akku-Belastung + Automatische Genauigkeit + Die Genauigkeit wird automatisch ausgewählt je nach Ladezustand des Geräts + Wähle \"Hohe Genauigkeit\" oder \"Akku sparen\" unten aus + Hohe Genauigkeit erzwingen + Hohe Genauigkeit, leert aber vermutlich den Akku schneller + Niedrigere Genauigkeit, aber bessere Akku-Lebensdauer + \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml new file mode 100644 index 0000000..fad766b --- /dev/null +++ b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,41 @@ + + + Doerflinger-Helpers + The action you would like to perform requires DeviceAdmin privileges, please grant them before continuing. + Welcome %s + Welcome User Whatever + Unknown User + Sleep Timer + Offers the possibility to set a sleep timer, after which the display will turn off + Device Admin privileges are needed for locking the screen after a specified timeout + In order for this feature to work, the app must be allowed to show a Notification + Sleep timer is scheduled. Your device will lock itself in %s + Cancel + Share Location + You are sharing your current location within the family + Currently nobody is sharing their location + You are currently not following anyone + You are following %s + Last Received Location = %f;%f + Last received altitude = %.2f Meters + The last received velocity was %.2f m/s (%.1f km/h) + This update was received at: %s + Share your location + In order to share your position also while in the background, please grant the always permission + Settings + Flashlight + In order to have access to the flashlight, you need to grant the Camera permission. + Activate flashlight on minimum brightness + Activate flashlight on medium brightness + Activate flashlight on maximum brightness + Settings + Share Location + Location Update frequency + The minimum amount of seconds to wait between two location updates Note: The lower the value the higher the battery consumption + Automatic accuracy detection + Accuracy will be auto selected based on the current charging state + Select high accuracy vs. balanced power consumption below + Force high accuracy + High accuracy for location data, may drain battery faster + Lower accuracy for location data, but better battery consumption + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 0000000..0537c72 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 6019e6e..8c7e7c4 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,4 +1,44 @@ 28sp + + + 10dp + + + 75dp + 75dp + 1 + + + 75dp + 40dp + 40dp + + + 75dp + 75dp + 50dp + 50dp + 2dp + 11sp + + + 250dp + 112dp + 200dp + 50dp + 8dp + 2dp + 25dp + 20dp + 30dp + 3 + 3 + + + 1 + 60 + 1 + 10 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ee87e48..3258fad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,5 @@ Doerflinger-Helpers - The action you would like to perform requires DeviceAdmin privileges, please grant them before continuing. @@ -10,29 +9,36 @@ Sleep Timer - SleepTimer - Offers the possibility top set a sleep timer, after which the display will turn off + SleepTimer + Offers the possibility to set a sleep timer, after which the display will turn off Device Admin privileges are needed for locking the screen after a specified timeout In order for this feature to work, the app must be allowed to show a Notification Sleep timer is scheduled. Your device will lock itself in %s Cancel - %02d:%02d:%02d" - %02d:%02d" + %02d:%02d:%02d" + %02d:%02d" Share Location - ShareLocation + ShareLocation You are sharing your current location within the family Currently nobody is sharing their location You are currently not following anyone You are following %s Last Received Location = %f;%f - Last received latitude = %.2f Meters + Last received altitude = %.2f Meters The last received velocity was %.2f m/s (%.1f km/h) This update was received at: %s Share your location In order to share your position also while in the background, please grant the always permission - SettingsActivity + Settings + + + Flashlight + In order to have access to the flashlight, you need to grant the Camera permission. + Activate flashlight on minimum brightness + Activate flashlight on medium brightness + Activate flashlight on maximum brightness Settings @@ -40,7 +46,7 @@ Share Location Location Update frequency - The minimum amount of seconds to wait between two location updates\nNote: The lower the value the higher the battery consumption + The minimum amount of seconds to wait between two location updates Note: The lower the value the higher the battery consumption Automatic accuracy detection Accuracy will be auto selected based on the current charging state Select high accuracy vs. balanced power consumption below diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index fed5b95..7870667 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -6,11 +6,11 @@