19 | fun getRoughRecordingTimeInMillis(): Long
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/RecorderConfig.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.recorder
2 |
3 | import com.nll.helper.server.ServerRecorderListener
4 | import java.io.File
5 |
6 | data class RecorderConfig(
7 | val encoder: Int,
8 | val recordingFile: File,
9 | val audioChannels: Int,
10 | val encodingBitrate: Int,
11 | val audioSamplingRate: Int,
12 | val audioSource: Int,
13 | val mediaRecorderOutputFormat: Int,
14 | val mediaRecorderAudioEncoder: Int,
15 | val recordingGain: Int,
16 | val serverRecorderListener: ServerRecorderListener
17 | ) {
18 | companion object {
19 | fun fromPrimitives(
20 | encoder: Int,
21 | recordingFile: File,
22 | audioChannels: Int,
23 | encodingBitrate: Int,
24 | audioSamplingRate: Int,
25 | audioSource: Int,
26 | mediaRecorderOutputFormat: Int,
27 | mediaRecorderAudioEncoder: Int,
28 | recordingGain: Int,
29 | serverRecorderListener: ServerRecorderListener
30 | ) = RecorderConfig(
31 | encoder = encoder,
32 | recordingFile = recordingFile,
33 | audioChannels = audioChannels,
34 | encodingBitrate = encodingBitrate,
35 | audioSamplingRate = audioSamplingRate,
36 | audioSource = audioSource,
37 | mediaRecorderOutputFormat = mediaRecorderOutputFormat,
38 | mediaRecorderAudioEncoder = mediaRecorderAudioEncoder,
39 | recordingGain = recordingGain,
40 | serverRecorderListener = serverRecorderListener
41 | )
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/Clock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.nll.helper.recorder.mediacodec;
17 |
18 | import android.os.Handler;
19 | import android.os.Looper;
20 |
21 | import androidx.annotation.Nullable;
22 |
23 | /**
24 | * An interface through which system clocks can be read and {@link HandlerWrapper}s created. The
25 | * {@link #DEFAULT} implementation must be used for all non-test cases.
26 | */
27 | public interface Clock {
28 |
29 | /**
30 | * Default {@link Clock} to use for all non-test cases.
31 | */
32 | Clock DEFAULT = new SystemClock();
33 |
34 | /**
35 | * Returns the current time in milliseconds since the Unix Epoch.
36 | *
37 | * @see System#currentTimeMillis()
38 | */
39 | long currentTimeMillis();
40 |
41 | /**
42 | * @see android.os.SystemClock#elapsedRealtime()
43 | */
44 | long elapsedRealtime();
45 |
46 | /**
47 | * @see android.os.SystemClock#uptimeMillis()
48 | */
49 | long uptimeMillis();
50 |
51 | /**
52 | * @see android.os.SystemClock#sleep(long)
53 | */
54 | void sleep(long sleepTimeMs);
55 |
56 | /**
57 | * Creates a {@link HandlerWrapper} using a specified looper and a specified callback for handling
58 | * messages.
59 | *
60 | * @see Handler#Handler(Looper, Handler.Callback)
61 | */
62 | HandlerWrapper createHandler(Looper looper, @Nullable Handler.Callback callback);
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/HandlerWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.nll.helper.recorder.mediacodec;
17 |
18 | import android.os.Handler;
19 | import android.os.Looper;
20 | import android.os.Message;
21 |
22 | import androidx.annotation.Nullable;
23 |
24 | /**
25 | * An interface to call through to a {@link Handler}. Instances must be created by calling {@link
26 | * Clock#createHandler(Looper, Handler.Callback)} on {@link Clock#DEFAULT} for all non-test cases.
27 | */
28 | public interface HandlerWrapper {
29 |
30 | /** @see Handler#getLooper() */
31 | Looper getLooper();
32 |
33 | /** @see Handler#obtainMessage(int) */
34 | Message obtainMessage(int what);
35 |
36 | /** @see Handler#obtainMessage(int, Object) */
37 | Message obtainMessage(int what, @Nullable Object obj);
38 |
39 | /** @see Handler#obtainMessage(int, int, int) */
40 | Message obtainMessage(int what, int arg1, int arg2);
41 |
42 | /** @see Handler#obtainMessage(int, int, int, Object) */
43 | Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj);
44 |
45 | /** @see Handler#sendEmptyMessage(int) */
46 | boolean sendEmptyMessage(int what);
47 |
48 | /** @see Handler#sendEmptyMessageAtTime(int, long) */
49 | boolean sendEmptyMessageAtTime(int what, long uptimeMs);
50 |
51 | /** @see Handler#removeMessages(int) */
52 | void removeMessages(int what);
53 |
54 | /** @see Handler#removeCallbacksAndMessages(Object) */
55 | void removeCallbacksAndMessages(@Nullable Object token);
56 |
57 | /** @see Handler#post(Runnable) */
58 | boolean post(Runnable runnable);
59 |
60 | /** @see Handler#postDelayed(Runnable, long) */
61 | boolean postDelayed(Runnable runnable, long delayMs);
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/MediaCodecInputBufferEnqueuer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2020 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.nll.helper.recorder.mediacodec;
18 |
19 |
20 | /** Abstracts operations to enqueue input buffer on a {@link android.media.MediaCodec}. */
21 | interface MediaCodecInputBufferEnqueuer {
22 |
23 | /**
24 | * Starts this instance.
25 | *
26 | * Call this method after creating an instance.
27 | */
28 | void start();
29 |
30 | /**
31 | * Submits an input buffer for decoding.
32 | *
33 | * @see android.media.MediaCodec#queueInputBuffer
34 | */
35 | void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
36 |
37 |
38 | /** Flushes the instance. */
39 | void flush();
40 |
41 | /** Shut down the instance. Make sure to call this method to release its internal resources. */
42 | void shutdown();
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/SynchronousMediaCodecAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.nll.helper.recorder.mediacodec;
18 |
19 | import android.media.MediaCodec;
20 | import android.media.MediaCrypto;
21 | import android.media.MediaFormat;
22 | import android.view.Surface;
23 |
24 | import androidx.annotation.Nullable;
25 |
26 | /**
27 | * A {@link MediaCodecAdapter} that operates the underlying {@link MediaCodec} in synchronous mode.
28 | */
29 | /* package */ final class SynchronousMediaCodecAdapter implements com.nll.helper.recorder.mediacodec.MediaCodecAdapter {
30 |
31 | private final MediaCodec codec;
32 |
33 | public SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
34 | this.codec = mediaCodec;
35 | }
36 |
37 | @Override
38 | public void configure(
39 | @Nullable MediaFormat mediaFormat,
40 | @Nullable Surface surface,
41 | @Nullable MediaCrypto crypto,
42 | int flags) {
43 | codec.configure(mediaFormat, surface, crypto, flags);
44 | }
45 |
46 | @Override
47 | public void start() {
48 | codec.start();
49 | }
50 |
51 | @Override
52 | public int dequeueInputBufferIndex() {
53 | return codec.dequeueInputBuffer(0);
54 | }
55 |
56 | @Override
57 | public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) {
58 | return codec.dequeueOutputBuffer(bufferInfo, 0);
59 | }
60 |
61 | @Override
62 | public MediaFormat getOutputFormat() {
63 | return codec.getOutputFormat();
64 | }
65 |
66 | @Override
67 | public void queueInputBuffer(
68 | int index, int offset, int size, long presentationTimeUs, int flags) {
69 | codec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
70 | }
71 |
72 | @Override
73 | public void flush() {
74 | codec.flush();
75 | }
76 |
77 | @Override
78 | public void shutdown() {}
79 |
80 | @Override
81 | public MediaCodec getCodec() {
82 | return codec;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/SynchronousMediaCodecBufferEnqueuer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2020 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.nll.helper.recorder.mediacodec;
18 |
19 | import android.media.MediaCodec;
20 |
21 | /**
22 | * A {@link MediaCodecInputBufferEnqueuer} that forwards queueing methods directly to {@link
23 | * MediaCodec}.
24 | */
25 | class SynchronousMediaCodecBufferEnqueuer implements com.nll.helper.recorder.mediacodec.MediaCodecInputBufferEnqueuer {
26 | private final MediaCodec codec;
27 |
28 | /**
29 | * Creates an instance that queues input buffers on the specified {@link MediaCodec}.
30 | *
31 | * @param codec The {@link MediaCodec} to submit input buffers to.
32 | */
33 | SynchronousMediaCodecBufferEnqueuer(MediaCodec codec) {
34 | this.codec = codec;
35 | }
36 |
37 | @Override
38 | public void start() {}
39 |
40 | @Override
41 | public void queueInputBuffer(
42 | int index, int offset, int size, long presentationTimeUs, int flags) {
43 | codec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
44 | }
45 |
46 | @Override
47 | public void flush() {}
48 |
49 | @Override
50 | public void shutdown() {}
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/SystemClock.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.nll.helper.recorder.mediacodec;
17 |
18 | import android.os.Handler;
19 | import android.os.Handler.Callback;
20 | import android.os.Looper;
21 |
22 | import androidx.annotation.Nullable;
23 |
24 | /**
25 | * The standard implementation of {@link Clock}, an instance of which is available via {@link
26 | * SystemClock#DEFAULT}.
27 | */
28 | public class SystemClock implements com.nll.helper.recorder.mediacodec.Clock {
29 |
30 | protected SystemClock() {}
31 |
32 | @Override
33 | public long currentTimeMillis() {
34 | return System.currentTimeMillis();
35 | }
36 |
37 | @Override
38 | public long elapsedRealtime() {
39 | return android.os.SystemClock.elapsedRealtime();
40 | }
41 |
42 | @Override
43 | public long uptimeMillis() {
44 | return android.os.SystemClock.uptimeMillis();
45 | }
46 |
47 | @Override
48 | public void sleep(long sleepTimeMs) {
49 | android.os.SystemClock.sleep(sleepTimeMs);
50 | }
51 |
52 | @Override
53 | public HandlerWrapper createHandler(Looper looper, @Nullable Callback callback) {
54 | return new SystemHandlerWrapper(new Handler(looper, callback));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/recorder/mediacodec/SystemHandlerWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.nll.helper.recorder.mediacodec;
17 |
18 | import android.os.Looper;
19 | import android.os.Message;
20 |
21 | import androidx.annotation.Nullable;
22 |
23 | /** The standard implementation of {@link HandlerWrapper}. */
24 | /* package */ final class SystemHandlerWrapper implements com.nll.helper.recorder.mediacodec.HandlerWrapper {
25 |
26 | private final android.os.Handler handler;
27 |
28 | public SystemHandlerWrapper(android.os.Handler handler) {
29 | this.handler = handler;
30 | }
31 |
32 | @Override
33 | public Looper getLooper() {
34 | return handler.getLooper();
35 | }
36 |
37 | @Override
38 | public Message obtainMessage(int what) {
39 | return handler.obtainMessage(what);
40 | }
41 |
42 | @Override
43 | public Message obtainMessage(int what, @Nullable Object obj) {
44 | return handler.obtainMessage(what, obj);
45 | }
46 |
47 | @Override
48 | public Message obtainMessage(int what, int arg1, int arg2) {
49 | return handler.obtainMessage(what, arg1, arg2);
50 | }
51 |
52 | @Override
53 | public Message obtainMessage(int what, int arg1, int arg2, @Nullable Object obj) {
54 | return handler.obtainMessage(what, arg1, arg2, obj);
55 | }
56 |
57 | @Override
58 | public boolean sendEmptyMessage(int what) {
59 | return handler.sendEmptyMessage(what);
60 | }
61 |
62 | @Override
63 | public boolean sendEmptyMessageAtTime(int what, long uptimeMs) {
64 | return handler.sendEmptyMessageAtTime(what, uptimeMs);
65 | }
66 |
67 | @Override
68 | public void removeMessages(int what) {
69 | handler.removeMessages(what);
70 | }
71 |
72 | @Override
73 | public void removeCallbacksAndMessages(@Nullable Object token) {
74 | handler.removeCallbacksAndMessages(token);
75 | }
76 |
77 | @Override
78 | public boolean post(Runnable runnable) {
79 | return handler.post(runnable);
80 | }
81 |
82 | @Override
83 | public boolean postDelayed(Runnable runnable, long delayMs) {
84 | return handler.postDelayed(runnable, delayMs);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/ClientContentProviderHelper.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import com.nll.helper.recorder.CLog
6 |
7 |
8 | /**
9 | *
10 | *These are commands/requests server (APH) sends to client (ACR Phone) and expects response
11 | * Client should implement a Content provider just like ServerContentProvider and respond to these commands/requests
12 | *
13 | */
14 | object ClientContentProviderHelper {
15 | private const val logTag = "CR_ClientContentProviderHelper"
16 |
17 | //Ignore can be private!
18 | const val clientAuthority = "com.nll.helper.ClientContentProvider" //Same as client manifest
19 |
20 | //Ignore can be private!
21 | const val clientMethodAskToConnect = "client_ask_to_connect"
22 |
23 | //Ignore can be private!
24 | const val clientMethodGetClientVersionData = "client_version_data"
25 |
26 | //Ignore can be private!
27 | const val clientPackageName = "com.nll.cb"
28 |
29 | //Ignore can be private!
30 | fun askToClientToConnect(context: Context) {
31 | CLog.log(logTag, "askToClientToConnect()")
32 |
33 | /**
34 | * Make sure we handle crash as user may not have main app installed.
35 | * Client in response calls attempts to connect by creating instance of
36 | * IRemoteService_Proxy(context.applicationContext, IRemoteService.SERVICE_INTENT)
37 | *
38 | * Our client implementation uses coroutines, fo instantiating IRemoteService_Proxy also attempts to connect.
39 | * See https://github.com/josesamuel/remoter#kotlin-support-with-suspend-functions
40 | */
41 | try {
42 | context.contentResolver.call(Uri.parse("content://$clientAuthority"), clientMethodAskToConnect, null, null)
43 | } catch (ignored: Exception) {
44 | }
45 |
46 | }
47 |
48 | //Ignore can be private!
49 | fun getClientVersionData(context: Context): ClientVersionData? {
50 |
51 | CLog.log(logTag, "getClientVersionData()")
52 | /**
53 | * Make sure we handle crash as user may not have helper installed
54 | */
55 | val cmdResult = try {
56 | context.contentResolver.call(Uri.parse("content://${clientAuthority}"), clientMethodGetClientVersionData, null, null)
57 | } catch (e: Exception) {
58 | null
59 | }
60 | return if (cmdResult != null) {
61 | val clientVersionData = ClientVersionData.fromBundle(cmdResult)
62 | CLog.log(logTag, "getClientVersionData() -> clientVersionData: $clientVersionData")
63 | clientVersionData
64 | } else {
65 | CLog.log(logTag, "getClientVersionData() -> clientVersionData: null")
66 | null
67 | }
68 |
69 | }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/ClientVersionData.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.os.Bundle
4 |
5 | data class ClientVersionData(val clientVersion: Int){
6 | fun toBundle() = Bundle().apply {
7 |
8 | putInt(ARG_CLIENT_VERSION, clientVersion)
9 |
10 |
11 | }
12 | companion object {
13 | private const val ARG_CLIENT_VERSION = "ARG_CLIENT_VERSION"
14 | fun fromBundle(bundle: Bundle?): ClientVersionData? {
15 | return bundle?.let {
16 | val clientVersion = it.getInt(ARG_CLIENT_VERSION, -1)
17 | ClientVersionData(clientVersion)
18 | }
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/IAccessibilityServiceBridge.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * Implementation for server is provided.
7 | * Client should implement no-op version of this interface in the same package
8 | *
9 | * While it won't be used, due to nature of AIDL, server package has to be in client code.
10 | * We use bridge package to provide no-op version of this interface to client app.
11 | */
12 | interface IAccessibilityServiceBridge {
13 | fun isHelperServiceEnabled(context: Context): Boolean
14 |
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/IRecorderBridge.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.content.Context
4 |
5 | /**
6 | * Must be copy of IRemoteService
7 | *
8 | * Implementation for server is provided.
9 | * Client should implement no-op version of this interface in the same package
10 | *
11 | * While it won't be used, due to nature of AIDL, server package has to be in client code.
12 | * We use bridge package to provide no-op version of this interface to client app.
13 | */
14 | interface IRecorderBridge {
15 | fun startRecording(
16 | context: Context,
17 | encoder: Int,
18 | recordingFile: String,
19 | audioChannels: Int,
20 | encodingBitrate: Int,
21 | audioSamplingRate: Int,
22 | audioSource: Int,
23 | mediaRecorderOutputFormat: Int,
24 | mediaRecorderAudioEncoder: Int,
25 | recordingGain: Int,
26 | serverRecorderListener: ServerRecorderListener
27 | ): Int
28 |
29 |
30 | fun stopRecording()
31 | fun pauseRecording()
32 | fun resumeRecording()
33 |
34 | fun showNeedsAudioRecordPermissionNotification(context: Context)
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/IRemoteService.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.os.IBinder
4 | import remoter.annotations.Oneway
5 | import remoter.annotations.ParamIn
6 | import remoter.annotations.Remoter
7 |
8 |
9 | /**
10 | *
11 | * See https://github.com/josesamuel/remoter
12 | *
13 | */
14 | @Remoter
15 | interface IRemoteService {
16 | suspend fun startRecording( encoder: Int,
17 | recordingFile: String,
18 | audioChannels: Int,
19 | encodingBitrate: Int,
20 | audioSamplingRate: Int,
21 | audioSource: Int,
22 | mediaRecorderOutputFormat: Int,
23 | mediaRecorderAudioEncoder: Int,
24 | recordingGain: Int) : Int
25 | suspend fun stopRecording()
26 | suspend fun pauseRecording()
27 | suspend fun resumeRecording()
28 | suspend fun registerClientProcessDeath(@ParamIn clientDeathListener: IBinder)
29 | @Oneway
30 | fun registerListener(listener: IRemoteServiceListener)
31 | @Oneway
32 | fun unRegisterListener(listener: IRemoteServiceListener)
33 | companion object {
34 | /**Intent to connect for this service. Will be used by the client.*/
35 | const val SERVICE_INTENT = "com.nll.helper.RemoteService"
36 | }
37 |
38 |
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/IRemoteServiceListener.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import remoter.annotations.Remoter
4 |
5 |
6 |
7 | /**
8 | *
9 | * See https://github.com/josesamuel/remoter
10 | *
11 | */
12 | @Remoter
13 | interface IRemoteServiceListener {
14 | fun onRecordingStateChange(recordingState: Int)
15 |
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/RecorderError.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | enum class RecorderError {
4 | AudioRecordInUse,
5 | MediaCodecQueueInputBufferFailed,
6 | AudioRecordReadFailed,
7 | EmptyInputBuffer,
8 | MediaMuxerWriteFailed,
9 | MediaRecorderError,
10 | MediaCodecException,
11 | RemoteError
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/RemoteResponseCodes.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | object RemoteResponseCodes {
4 | const val HELPER_IS_NOT_RUNNING = -3
5 | //Ignore unused
6 | const val REMOTE_AIDL_ERROR = -2
7 | const val REMOTE_ERROR = -1
8 | const val REMOTE_OK = 0
9 |
10 | const val RECORDING_PAUSED = 1
11 | const val RECORDING_RECORDING= 2
12 | const val RECORDING_STOPPED= 3
13 | const val RECORDING_ERROR= 4
14 |
15 |
16 |
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/RemoteService.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.content.Intent
4 | import android.os.IBinder
5 | import androidx.lifecycle.LifecycleService
6 | import com.nll.helper.recorder.CLog
7 | import kotlinx.coroutines.flow.MutableStateFlow
8 | import kotlinx.coroutines.flow.asStateFlow
9 | import kotlin.properties.Delegates
10 |
11 | /**
12 | * Wrapper for IRemoteService_Proxy
13 | */
14 | class RemoteService : LifecycleService() {
15 | private val logTag = "CR_RemoteService (${Integer.toHexString(System.identityHashCode(this))})"
16 |
17 | /**
18 | * We need to make sure we always provide same instance in case client re connects.
19 | * Without it (although RemoteServiceImpl receives callback and stops recording at RemoteServiceImpl#registerClientProcessDeath) we may have a dangling instance of this class recording continuously!
20 | */
21 | private val remoteServiceStub: IRemoteService_Stub by lazy {
22 | IRemoteService_Stub(RemoteServiceImpl(applicationContext))
23 | }
24 |
25 | override fun onCreate() {
26 | super.onCreate()
27 | CLog.log(logTag, "onCreate()")
28 | }
29 |
30 | override fun onDestroy() {
31 | super.onDestroy()
32 | CLog.log(logTag, "onDestroy()")
33 | }
34 |
35 | override fun onBind(intent: Intent): IBinder {
36 | super.onBind(intent)
37 | CLog.log(logTag, "onBind()")
38 | connectionCount++
39 | return remoteServiceStub
40 | }
41 |
42 | override fun onUnbind(intent: Intent): Boolean {
43 | connectionCount--
44 | CLog.log(logTag, "onUnbind()")
45 | return super.onUnbind(intent)
46 | }
47 |
48 | companion object {
49 | private val clientConnectionCount = MutableStateFlow(0)
50 | fun observeClientConnectionCount() = clientConnectionCount.asStateFlow()
51 |
52 | private var connectionCount by Delegates.observable(0) { _, oldValue, newValue ->
53 | clientConnectionCount.tryEmit(newValue)
54 | }
55 |
56 | }
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/ServerRecorderListener.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | interface ServerRecorderListener {
4 | fun onRecordingStateChange(newState: ServerRecordingState)
5 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/ServerRecordingState.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | sealed class ServerRecordingState {
4 | companion object {
5 | fun fromResponseCode(code: Int) = when (code) {
6 | RemoteResponseCodes.RECORDING_ERROR -> Error(RecorderError.RemoteError, Exception("Remote error"))
7 | RemoteResponseCodes.RECORDING_PAUSED -> Paused
8 | RemoteResponseCodes.RECORDING_RECORDING -> Recording
9 | RemoteResponseCodes.RECORDING_STOPPED -> Stopped
10 | else -> throw (java.lang.IllegalArgumentException("Unknown RemoteResponseCode ($code)"))
11 |
12 | }
13 | }
14 |
15 | fun asResponseCode() = when (this) {
16 | is Error -> RemoteResponseCodes.RECORDING_ERROR
17 | Paused -> RemoteResponseCodes.RECORDING_PAUSED
18 | Recording -> RemoteResponseCodes.RECORDING_RECORDING
19 | Stopped -> RemoteResponseCodes.RECORDING_STOPPED
20 | HelperIsNotRunning -> RemoteResponseCodes.HELPER_IS_NOT_RUNNING
21 | RemoteAIDLError -> RemoteResponseCodes.REMOTE_AIDL_ERROR
22 | }
23 |
24 |
25 | object Recording : ServerRecordingState()
26 | object Stopped : ServerRecordingState()
27 | object Paused : ServerRecordingState()
28 | object HelperIsNotRunning : ServerRecordingState()
29 | object RemoteAIDLError : ServerRecordingState()
30 | class Error(val recorderError: RecorderError, val exception: Exception) : ServerRecordingState()
31 |
32 | override fun toString(): String {
33 | return when (this) {
34 | Recording -> "Recording"
35 | Stopped -> "Stopped"
36 | Paused -> "Paused"
37 | HelperIsNotRunning -> "HelperIsNotRunning"
38 | RemoteAIDLError -> "RemoteAIDLError"
39 | is Error -> "Error ($exception)"
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/server/ServerVersionData.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.server
2 |
3 | import android.os.Bundle
4 |
5 | data class ServerVersionData(val serverVersion: Int) {
6 |
7 | fun toBundle() = Bundle().apply {
8 |
9 | putInt(ARG_SERVER_VERSION, serverVersion)
10 |
11 |
12 |
13 | }
14 |
15 | companion object {
16 | private const val ARG_SERVER_VERSION = "ARG_SERVER_VERSION"
17 | fun fromBundle(bundle: Bundle?): ServerVersionData? {
18 | return bundle?.let {
19 | val serverVersion = it.getInt(ARG_SERVER_VERSION, -1)
20 | ServerVersionData(serverVersion)
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/support/AccessibilityChangeObserver.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.support
2 |
3 | import android.content.Context
4 | import android.database.ContentObserver
5 | import android.provider.Settings
6 | import com.nll.helper.recorder.CLog
7 |
8 | object AccessibilityChangeObserver {
9 | private const val logTag = "CR_AccessibilityChangeObserver"
10 | fun registerAccessibilityServiceChangeContentObserver(context: Context) {
11 | CLog.log(logTag, "registerAccessibilityServiceChangeContentObserver()")
12 |
13 | context.contentResolver.registerContentObserver(Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES), false, object : ContentObserver(null) {
14 | override fun onChange(self: Boolean) {
15 |
16 | val isAccessibilityServiceEnabledNow = AccessibilityCallRecordingService.isHelperServiceEnabled(context)
17 |
18 | CLog.log(logTag, "onChange() -> self: $self, isAccessibilityServiceEnabledNow: $isAccessibilityServiceEnabledNow")
19 |
20 | if (!isAccessibilityServiceEnabledNow) {
21 | CLog.log(logTag, "onChange() -> Warn user that AccessibilityService is not enabled on CallRecordingSupportType that needs it")
22 | AccessibilityCallRecordingService.postEnableHelperServiceNotificationAndToast(context, false)
23 | }
24 | if(isAccessibilityServiceEnabledNow){
25 | AccessibilityCallRecordingService.startHelperServiceIfIsNotRunning(context)
26 | }
27 | }
28 | })
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/support/AccessibilityChangeObserverInitiator.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.support
2 |
3 | import android.content.Context
4 | import androidx.startup.Initializer
5 | import com.nll.helper.recorder.CLog
6 |
7 |
8 | /**
9 | * Used to observer Accessibility Service changes
10 | */
11 | class AccessibilityChangeObserverInitiator : Initializer {
12 | private val logTag = "CR_AccessibilityChangeObserverInitiator"
13 |
14 | override fun create(context: Context) {
15 | CLog.log(logTag, "create()")
16 | AccessibilityChangeObserver.registerAccessibilityServiceChangeContentObserver(context)
17 |
18 | }
19 |
20 | override fun dependencies(): List>> {
21 | // No dependencies on other libraries.
22 | return emptyList()
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/ui/DialogTerms.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.ui
2 |
3 | import android.app.Dialog
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.os.Bundle
7 | import android.widget.Toast
8 | import androidx.appcompat.app.AlertDialog
9 | import androidx.fragment.app.DialogFragment
10 | import androidx.fragment.app.FragmentManager
11 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
12 | import com.nll.helper.R
13 | import com.nll.helper.StoreConfigImpl
14 | import com.nll.helper.databinding.DialogTermsBinding
15 | import com.nll.helper.recorder.CLog
16 | import com.nll.helper.util.autoCleared
17 | import com.nll.helper.util.extSetHTML
18 |
19 | class DialogTerms : DialogFragment() {
20 | private var listener: Listener? = null
21 |
22 | private var binding by autoCleared()
23 |
24 |
25 | companion object {
26 | private const val logTag = "DialogTerms"
27 |
28 | private const val fragmentTag = "terms-dialog"
29 |
30 | fun display(fragmentManager: FragmentManager, listener: Listener) {
31 | val fragment = DialogTerms().apply {
32 | this.listener = listener
33 | }
34 | fragment.show(fragmentManager, fragmentTag)
35 | }
36 | }
37 |
38 | fun interface Listener {
39 | fun onTermsChoice(accepted: Boolean)
40 | }
41 |
42 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
43 | binding = DialogTermsBinding.inflate(requireActivity().layoutInflater)
44 | val message = String.format(getString(R.string.privacy_policy_warning), StoreConfigImpl.getPrivacyPolicyUrl())
45 | binding.termsAgreed.extSetHTML(true, message) { urlToOpen ->
46 | try {
47 | startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(urlToOpen)))
48 | } catch (e: Exception) {
49 | CLog.logPrintStackTrace(e)
50 | Toast.makeText(requireContext(), e.message, Toast.LENGTH_SHORT).show()
51 | }
52 | }
53 |
54 |
55 | val alertDialog = MaterialAlertDialogBuilder(requireContext(), theme)
56 | .setCancelable(false)
57 | .setView(binding.root)
58 | .setPositiveButton(R.string.agree) { _, _ ->
59 | listener?.onTermsChoice(binding.termsAgreed.isChecked)
60 | }
61 | .setNegativeButton(android.R.string.cancel) { _, _ ->
62 | listener?.onTermsChoice(false)
63 | }
64 | .create()
65 |
66 |
67 | binding.termsAgreed.setOnCheckedChangeListener { _, isChecked ->
68 | alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = isChecked
69 | }
70 |
71 | alertDialog.setOnShowListener {
72 | alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
73 | }
74 |
75 | return alertDialog
76 | }
77 |
78 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/ui/MainActivityViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.ui
2 |
3 | import android.app.Application
4 | import androidx.lifecycle.AndroidViewModel
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.ViewModel
8 | import androidx.lifecycle.ViewModelProvider
9 | import androidx.lifecycle.viewModelScope
10 | import com.nll.helper.recorder.CLog
11 | import com.nll.helper.server.RemoteService
12 | import com.nll.helper.support.AccessibilityCallRecordingService
13 | import kotlinx.coroutines.flow.launchIn
14 | import kotlinx.coroutines.flow.onEach
15 |
16 |
17 | class MainActivityViewModel(app: Application) : AndroidViewModel(app) {
18 | private val logTag = "CR_MainActivityViewModel"
19 |
20 | private val _accessibilityServicesChanged = MutableLiveData()
21 | fun observeAccessibilityServicesChanges(): LiveData = _accessibilityServicesChanged
22 |
23 | private val _clientConnected = MutableLiveData()
24 | fun observeClientConnected(): LiveData = _clientConnected
25 |
26 |
27 | init {
28 |
29 |
30 |
31 | val isHelperServiceEnabledOnInit = AccessibilityCallRecordingService.isHelperServiceEnabled(app)
32 | CLog.log(logTag, "init() -> isHelperServiceEnabledOnInit: $isHelperServiceEnabledOnInit")
33 |
34 | _accessibilityServicesChanged.postValue(isHelperServiceEnabledOnInit)
35 |
36 | AccessibilityCallRecordingService.observeAccessibilityServicesChangesLiveData().observeForever { isEnabled ->
37 | CLog.log(logTag, "observeAccessibilityServicesChangesLiveData() -> isEnabled: $isEnabled")
38 | _accessibilityServicesChanged.postValue(isEnabled!!)
39 | }
40 |
41 | RemoteService.observeClientConnectionCount().onEach { connectionCount ->
42 | CLog.log(logTag, "observeClientConnectionCount() -> connectionCount: $connectionCount")
43 | _clientConnected.postValue(connectionCount > 0)
44 | }.launchIn(viewModelScope)
45 | }
46 |
47 |
48 | @Suppress("UNCHECKED_CAST")
49 | class Factory(private val application: Application) : ViewModelProvider.Factory {
50 | override fun create(modelClass: Class): T {
51 | return MainActivityViewModel(application) as T
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/HttpProvider.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import com.nll.helper.BuildConfig
4 | import com.nll.helper.recorder.CLog
5 | import okhttp3.OkHttpClient
6 | import okhttp3.Request
7 | import okhttp3.logging.HttpLoggingInterceptor
8 | import java.util.concurrent.TimeUnit
9 |
10 | object HttpProvider {
11 | private const val logTag = "HttpProvider"
12 | private const val connectionTimeoutMs: Long = 10000
13 | private const val readTimeoutMs: Long = 50000
14 | private const val writeTimeoutMs: Long = 50000
15 | private val client: OkHttpClient by lazy {
16 | val loggingInterceptor = HttpLoggingInterceptor { message ->
17 | if (CLog.isDebug()) {
18 | CLog.log(logTag, message)
19 | }
20 | }.apply {
21 | val logLevel = if (BuildConfig.DEBUG) {
22 | HttpLoggingInterceptor.Level.BODY
23 | } else {
24 | HttpLoggingInterceptor.Level.NONE
25 | }
26 | setLevel(logLevel)
27 | //Security
28 | //redactHeader("Authorization");
29 | //redactHeader("Cookie");
30 | }
31 |
32 | OkHttpClient().newBuilder()
33 | .addInterceptor(loggingInterceptor)
34 | .connectTimeout(connectionTimeoutMs, TimeUnit.MILLISECONDS)
35 | .readTimeout(readTimeoutMs, TimeUnit.MILLISECONDS)
36 | .writeTimeout(writeTimeoutMs, TimeUnit.MILLISECONDS).build()
37 | }
38 |
39 |
40 | fun provideOkHttpClient(): OkHttpClient {
41 | return client
42 | }
43 |
44 | fun provideRequestForOwnServer(url: String): Request.Builder {
45 | return Request.Builder()
46 | .header("User-Agent", "ACR Phone") // Do not change. Keeps these as server requests validated according to these
47 | .header("Accept", "*/*")
48 | .url(url)
49 |
50 |
51 | }
52 |
53 | fun provideRequest(url: String): Request.Builder {
54 | return Request.Builder()
55 | .header("User-Agent", "ACR Phone")
56 | .header("Accept", "*/*")
57 | .url(url)
58 |
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/UpdateChecker.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import android.content.Context
4 | import com.nll.helper.recorder.CLog
5 | import com.nll.helper.update.version.RemoteAppVersion
6 | import com.nll.helper.util.AppSettings
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.delay
9 | import kotlinx.coroutines.withContext
10 |
11 | object UpdateChecker {
12 | private const val logTag = "UpdateChecker"
13 |
14 |
15 | suspend fun checkUpdate(context: Context): UpdateResult = withContext(Dispatchers.IO) {
16 | //Init settings
17 | AppSettings.initIfNeeded(context)
18 | val updateRequest = UpdateRequestImpl()
19 |
20 | if (updateRequest.shouldCheckUpdate()) {
21 | if (CLog.isDebug()) {
22 | CLog.log(logTag, "checkUpdate() -> Check updates from server after delay and return up to date result")
23 | }
24 | delay(updateRequest.getUpdateCheckDelayInMillis())
25 | realDownloadUpdate(updateRequest)
26 | } else {
27 | if (CLog.isDebug()) {
28 | CLog.log(logTag, "checkUpdate() -> No update check needed. Return previous update result")
29 | }
30 | }
31 |
32 | updateRequest.getUpdateRequestResult()
33 | }
34 |
35 |
36 | private fun realDownloadUpdate(updateRequest: UpdateRequestImpl) {
37 | if (CLog.isDebug()) {
38 | CLog.log(logTag, "realDownloadUpdate")
39 | }
40 | try {
41 | HttpProvider.provideOkHttpClient().newCall(HttpProvider.provideRequestForOwnServer(updateRequest.getUpdateCheckUrl()).build()).execute().use { response ->
42 | if (response.isSuccessful) {
43 | if (CLog.isDebug()) {
44 | CLog.log(logTag, "realDownloadUpdate -> response.isSuccessful. Save last update check time")
45 | }
46 | updateRequest.saveLastUpdateCheckTime()
47 |
48 | val bodyString = response.body.string()
49 | if (CLog.isDebug()) {
50 | CLog.log(logTag, "realDownloadUpdate -> response.body: $bodyString")
51 | }
52 |
53 | try {
54 | val remoteVersion = RemoteAppVersion(bodyString)
55 | if (CLog.isDebug()) {
56 | CLog.log(logTag, "realDownloadUpdate success. Save remoteVersion: $remoteVersion")
57 | }
58 | updateRequest.saveRemoteVersion(bodyString)
59 | } catch (e: Exception) {
60 | if (CLog.isDebug()) {
61 | CLog.log(logTag, "realDownloadUpdate failed while parsing response. Will try again later")
62 | }
63 | CLog.logPrintStackTrace(e)
64 | }
65 |
66 |
67 | }
68 | }
69 | } catch (e: Exception) {
70 | CLog.logPrintStackTrace(e)
71 | }
72 | }
73 |
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/UpdateRequest.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import com.nll.helper.update.version.RemoteAppVersion
4 |
5 |
6 | interface UpdateRequest {
7 | fun getUpdateCheckUrl(): String
8 | fun saveRemoteVersion(remoteVersionJson: String)
9 | fun saveLastUpdateCheckTime()
10 | fun getUpdateRequestResult(): UpdateResult
11 | fun getRemoteVersionInfo(): RemoteAppVersion?
12 | fun shouldCheckUpdate(): Boolean
13 | //Some delay different then MessageRequest so that we do not bombard the server with 2 request on first install
14 | fun getUpdateCheckDelayInMillis() = 200L
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/UpdateResult.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import android.content.Context
4 | import com.nll.helper.update.version.RemoteAppVersion
5 |
6 |
7 | sealed class UpdateResult {
8 | class Required(val remoteAppVersion: RemoteAppVersion, val forceUpdate: Boolean) : UpdateResult(){
9 | fun openDownloadUrl(context: Context) {
10 | DownloadUrlOpenerImpl.openDownloadUrl(context, remoteAppVersion)
11 | }
12 | }
13 |
14 | /**
15 | * No update required
16 | */
17 | object NotRequired : UpdateResult()
18 |
19 | override fun toString(): String {
20 | return when (this) {
21 | is NotRequired -> "NotRequired"
22 | is Required -> "Required(forceUpdate: $forceUpdate)"
23 |
24 | }
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/contract/IDownloadUrlOpener.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update.contract
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import com.nll.helper.update.version.RemoteAppVersion
6 |
7 | interface IDownloadUrlOpener {
8 | fun getOpenDownloadUrlIntent(context: Context, remoteAppVersion: RemoteAppVersion): Intent
9 | fun openDownloadUrl(context: Context, remoteAppVersion: RemoteAppVersion)
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/version/LocalAppVersion.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update.version
2 |
3 | import com.nll.helper.BuildConfig
4 |
5 | object LocalAppVersion {
6 |
7 | const val versionCode = BuildConfig.VERSION_CODE
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/update/version/RemoteAppVersion.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update.version
2 |
3 | import org.json.JSONObject
4 | /*
5 | Expected Json response format is
6 | {
7 | "versionCode": 1,
8 | "downloadUrl": "https://acr.app/aph.apk",
9 | "whatsNewMessage": "",
10 | "forceUpdate": false
11 | }
12 |
13 | */
14 | class RemoteAppVersion(json: String) : JSONObject(json) {
15 | val versionCode = this.optInt("versionCode")
16 | val downloadUrl: String = this.optString("downloadUrl")
17 | val whatsNewMessage: String = this.optString("whatsNewMessage")
18 | val forceUpdate: Boolean = this.optBoolean("forceUpdate")
19 |
20 | override fun toString(): String {
21 | return "RemoteVersionInfo(versionCode=$versionCode, downloadUrl='$downloadUrl', whatsNewMessage='$whatsNewMessage', forceUpdate='$forceUpdate')"
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/util/AppSettings.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.util
2 |
3 |
4 | import android.content.Context
5 | import com.chibatching.kotpref.Kotpref
6 | import com.chibatching.kotpref.KotprefModel
7 |
8 | object AppSettings : KotprefModel() {
9 | fun initIfNeeded(context: Context) {
10 | if (!Kotpref.isInitialized) {
11 | Kotpref.init(context.applicationContext)
12 | }
13 | }
14 |
15 | //For mapping KotPref to sharedPref used in SettingsFragment
16 | //Mapping to preference fragments done in BasePreferenceCompatFragment
17 | override val kotprefName: String = "app-recorder"
18 |
19 |
20 | var remoteVersionJson: String by stringPref(
21 | key = "remoteVersionJson",
22 | default = ""
23 | )
24 | var remoteVersionLastUpdateCheck: Long by longPref(
25 | key = "remoteVersionLastUpdateCheck",
26 | default = 0
27 | )
28 |
29 | /**
30 | *
31 | * As per
32 | * https://developer.android.com/guide/components/activities/background-starts
33 | * https://developer.android.com/about/versions/oreo/background
34 | *
35 | * Since client connects to server with AIDL bound service, helper app is excluded from background restrictions
36 | * as long as client app is in the background. In our case, since our app is bound by system telecom service
37 | * we are safe
38 | *
39 | * We however still have give option to users to start foreground service just in case some devices behave badly
40 | *
41 | */
42 | var actAsForegroundService: Boolean by booleanPref(
43 | key = "actAsForegroundService",
44 | default = false
45 | )
46 | var privacyPolicyAccepted: Boolean by booleanPref(
47 | key = "privacyPolicyAccepted",
48 | default = false
49 | )
50 |
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/util/AutoClearedValue.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.util
2 |
3 | /*
4 | * Copyright (C) 2018 The Android Open Source Project
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 |
20 | import androidx.fragment.app.Fragment
21 | import androidx.lifecycle.DefaultLifecycleObserver
22 | import androidx.lifecycle.LifecycleOwner
23 | import androidx.lifecycle.Observer
24 | import kotlin.properties.ReadWriteProperty
25 | import kotlin.reflect.KProperty
26 |
27 | /**
28 | * Updated fix via https://medium.com/@Zhuinden/an-update-to-the-fragmentviewbindingdelegate-the-bug-weve-inherited-from-autoclearedvalue-7fc0a89fcae1
29 | * Pull request https://github.com/Zhuinden/architecture-components-samples/commit/5c8fb401a4c907089d201ab905366bb2546acb3e
30 | *
31 | * A lazy property that gets cleaned up when the fragment's view is destroyed.
32 | *
33 | * Accessing this variable while the fragment's view is destroyed will throw NPE.
34 | */
35 | class AutoClearedValue(val fragment: Fragment) : ReadWriteProperty {
36 | private var _value: T? = null
37 |
38 | init {
39 | val viewLifecycleOwnerLiveDataObserver = Observer {
40 | val viewLifecycleOwner = it ?: return@Observer
41 |
42 | viewLifecycleOwner.lifecycle.addObserver(object: DefaultLifecycleObserver {
43 | override fun onDestroy(owner: LifecycleOwner) {
44 | _value = null
45 | }
46 | })
47 | }
48 |
49 | fragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
50 | override fun onCreate(owner: LifecycleOwner) {
51 | fragment.viewLifecycleOwnerLiveData.observeForever(viewLifecycleOwnerLiveDataObserver)
52 | }
53 |
54 | override fun onDestroy(owner: LifecycleOwner) {
55 | fragment.viewLifecycleOwnerLiveData.removeObserver(viewLifecycleOwnerLiveDataObserver)
56 | }
57 | })
58 | }
59 |
60 | override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
61 | return _value ?: throw IllegalStateException(
62 | "should never call auto-cleared-value get when it might not be available"
63 | )
64 | }
65 |
66 | override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
67 | _value = value
68 | }
69 | }
70 |
71 | /**
72 | * Creates an [AutoClearedValue] associated with this fragment.
73 | */
74 | fun Fragment.autoCleared() = AutoClearedValue(this)
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/util/LiveEvent.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.util
2 |
3 | import androidx.annotation.MainThread
4 | import androidx.collection.ArraySet
5 | import androidx.lifecycle.LifecycleOwner
6 | import androidx.lifecycle.MediatorLiveData
7 | import androidx.lifecycle.Observer
8 |
9 | //https://github.com/hadilq/LiveEvent
10 |
11 |
12 | open class LiveEvent : MediatorLiveData() {
13 | object Event
14 | private val observers = ArraySet>()
15 |
16 | @MainThread
17 | override fun observe(owner: LifecycleOwner, observer: Observer) {
18 | observers.find { it.observer === observer }?.let { _ -> // existing
19 | return
20 | }
21 | val wrapper = ObserverWrapper(observer)
22 | observers.add(wrapper)
23 | super.observe(owner, wrapper)
24 | }
25 |
26 | @MainThread
27 | override fun observeForever(observer: Observer) {
28 | observers.find { it.observer === observer }?.let { _ -> // existing
29 | return
30 | }
31 | val wrapper = ObserverWrapper(observer)
32 | observers.add(wrapper)
33 | super.observeForever(wrapper)
34 | }
35 |
36 | @MainThread
37 | override fun removeObserver(observer: Observer) {
38 | if (observer is ObserverWrapper && observers.remove(observer)) {
39 | super.removeObserver(observer)
40 | return
41 | }
42 | val iterator = observers.iterator()
43 | while (iterator.hasNext()) {
44 | val wrapper = iterator.next()
45 | if (wrapper.observer == observer) {
46 | iterator.remove()
47 | super.removeObserver(wrapper)
48 | break
49 | }
50 | }
51 | }
52 |
53 | @MainThread
54 | override fun setValue(t: T?) {
55 | observers.forEach { it.newValue() }
56 | super.setValue(t)
57 | }
58 |
59 | private class ObserverWrapper(val observer: Observer) : Observer {
60 |
61 | private var pending = false
62 |
63 | override fun onChanged(value: T) {
64 | if (pending) {
65 | pending = false
66 | observer.onChanged(value)
67 | }
68 | }
69 |
70 | fun newValue() {
71 | pending = true
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/nll/helper/util/Util.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.util
2 |
3 |
4 | import android.content.Context
5 | import com.nll.helper.recorder.CLog
6 |
7 |
8 | object Util {
9 | fun dpToPx(context: Context, dp: Float): Float {
10 | return dp * context.resources.displayMetrics.density
11 | }
12 |
13 | fun getVersionCode(context: Context): Long {
14 | return try {
15 | context.applicationContext.packageManager.getPackageInfo(context.applicationContext.packageName, 0).longVersionCode
16 | } catch (e: Exception) {
17 | CLog.logPrintStackTrace(e)
18 | 0L
19 | }
20 | }
21 | fun isAppInstalled(context: Context, packageName: String): Boolean {
22 | return try {
23 | context.applicationContext.packageManager.getPackageInfo(packageName, 0)!=null
24 | } catch (e: Exception) {
25 | CLog.logPrintStackTrace(e)
26 | false
27 | }
28 | }
29 |
30 | fun getVersionName(context: Context): String {
31 | return try {
32 | context.applicationContext.packageManager.getPackageInfo(context.applicationContext.packageName, 0).versionName ?: "Cannot get version name!"
33 | } catch (e: Exception) {
34 | CLog.logPrintStackTrace(e)
35 | "Cannot get version name!"
36 | }
37 | }
38 |
39 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_cancel_24.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/crash_log_discard.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/crash_log_send.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_empty_icon.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_green_checked_24dp.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_helper_notification2.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_info_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_red_error_24dp.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_warning_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/notification_debug.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_terms.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/row_debug_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main_activity_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_helper_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_helper_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_helper_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-hdpi/ic_helper_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_helper_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-hdpi/ic_helper_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_helper_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-hdpi/ic_helper_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_helper_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-mdpi/ic_helper_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_helper_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-mdpi/ic_helper_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_helper_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-mdpi/ic_helper_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_helper_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xhdpi/ic_helper_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_helper_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xhdpi/ic_helper_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_helper_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xhdpi/ic_helper_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_helper_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xxhdpi/ic_helper_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_helper_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xxhdpi/ic_helper_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_helper_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xxhdpi/ic_helper_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_helper_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xxxhdpi/ic_helper_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_helper_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xxxhdpi/ic_helper_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_helper_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/app/src/main/res/mipmap-xxxhdpi/ic_helper_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-af/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH het omgeval
6 | Daar was \'n fout wat veroorsaak het dat APH omgeval en gestop het.\nHelp ons asseblief om dit reg te stel deur vir ons die fout-data te stuur, al wat jy moet doen is om op \'GOED\' te tik en hierdie verslag met e-pos te stuur.
7 | Al die toestemmings is nodig vir volle funksionaliteit
8 | Toestemmingsversoek
9 | Stuur
10 | Kanselleer
11 | Omval kennisgewing
12 | Waarskuwing!
13 |
14 |
15 | Nuwe weergawe gevind
16 | Kan nie enige toep kry om hierdie skakel oop te maak nie
17 | Kan nie hierdie skakel ontleed nie
18 |
19 |
20 | Skakel aan
21 |
22 |
23 | Tik op Laat Toe om die vereiste toestemmings vir oproepopname toe te laat
24 |
25 |
26 | Oproep Opnemer Hulp
27 | Hierdie diens word gebruik vir toegang tot oproepklank. Geen oproepe kan opgeneem word as dit af is nie. Om toegang tot jou data te beperk, beperk die toep waarnemings tot sy eie gebeurtenisse en versoek geen funksies nie\n\nSommige fone kan rapporteer dat die diens nie werk nie. Probeer om dit af en weer aan te skakel, as dit gebeur.
28 | Tik hier om oproep opnemer hulp aan te skakel
29 | Skakel asseblief Oproep Opnemer Hulp aan op die skerm vir geïnstalleerde toeganklikheidsdienste
30 | Oproep Opnemer Hulp is afgeskakel
31 |
32 |
33 | Laai af
34 | Kennisgewing
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ar/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | لقد تعطل APH
6 | حدث خطأ تسبب في تعطل APH وإيقافه.\nيُرجى مساعدتنا في إصلاح هذا العطل عن طريق إرسال بيانات الخطأ إلينا، كل ما عليك فعله هو النقر فوق \"موافق\" وإرسال هذا التقرير عبر البريد الإلكتروني.
7 | جميع الأذونات مطلوبة للوظائف الكاملة
8 | طلب إذن
9 | إرسال
10 | إلغاء
11 | إشعار الأعطال
12 | تحذير!
13 |
14 |
15 | تم العثور على إصدار جديد
16 | لا يمكن العثور على أي تطبيق لفتح هذا الرابط
17 | تعذر تحليل هذا الرابط
18 |
19 |
20 | ممكن
21 |
22 |
23 | اضغط على السماح لرؤية ومنح الأذونات المطلوبة لتسجيل المكالمات
24 |
25 |
26 | مساعد تسجيل المكالمات
27 | تُستخدم هذه الخدمة للوصول إلى صوت المكالمات. لا يمكن تسجيل صوت المكالمة عند تعطيلها. من أجل تقييد الوصول إلى بياناتك، يقيد التطبيق المراقبة على الأحداث الخاصة به ولا يطلب أي إمكانية قد تبلغ بعض الهواتف عن الخدمة على أنها لا تعمل. حاول تعطيله وتمكينه مرة أخرى إذا حدث ذلك.
28 | انقر هنا لتمكين مساعد تسجيل المكالمات
29 | يرجى تمكين مساعد تسجيل المكالمات في شاشة خدمات الوصول المثبتة
30 | لم يتم تمكين مساعد تسجيل المكالمات
31 | إشعار
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values-az/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH qəzaya uğrayıb
6 | APH-un qəzaya uğramasına və dayanmasına səbəb olan xəta oldu.\nLütfən səhv məlumatları göndərməklə onu düzəltməyə kömək edin, sadəcə \'OK\' vurun və bu hesabatı e-poçtla göndərin.
7 | Tam işləmə üçün bütün icazələr tələb olunur
8 | İcazə istəyi
9 | Göndər
10 | İmtina
11 | Qəza xəbərdarlığı
12 | Xəbərdarlıq!
13 |
14 |
15 | Yeni versiya tapıldı
16 | Bu linki açmaq üçün heç bir tətbiq tapılmadı
17 | Bu linki analiz etmək olmur
18 |
19 |
20 | Aktiv
21 |
22 |
23 | Zəng qeydləri üçün lazımi icazələri görmək və vermək üçün İcazə ver-ə toxunun
24 |
25 |
26 | Zəng qeydi köməkçisi
27 | Bu xidmət zəng səslərinə daxil olmaq üçün istifadə olunur. Bağlı olduqda heç bir səs qeydi edilə bilməz. Verilərinizə girişi məhdudlaşdırmaq üçün tətbiqetmə hadisələri ilə müşahidəçiliyi məhdudlaşdırır və hər hansı bir qabiliyyət tələb etmir\n\nBəzi telefonlar xidməti işləmədiyini bildirə bilər. Bu baş verərsə, onu söndürməyə və yenidən işə salmağa çalışın.
28 | Zəng Qeyd Köməkçisini aktiv etmək üçün bura toxunun
29 | Lütfən Yüklənmiş Əlçatan Xidmətləri ekranında Zəng Qeyd Köməkçisini aktiv edin
30 | Zəng Qeydi Köməkçisi aktiv edilməyib
31 |
32 |
33 | Endir
34 |
35 |
36 | ACR Telefonun Google Play Mağaza versiyası quraşdırılmayıb və ya yenilənməlidir. Bu proqram yalnız ACR Telefonunun uyğun versiyası ilə birlikdə istifadə edilə bilər
37 | Quraşdır
38 | ACR Telefonunun zəng yazma funksiyası üçün işlək vəziyyətdə saxlanılmalıdır
39 | Bu proqram, düzgün işləməsi üçün güncəllənməlidir
40 | Audio qeyd icazəsi
41 | Bildiriş
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-bg/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Телефон се затвори
6 | Поради грешка APH се затвори.
7 | Моля да ни помогнете да поправим грешката, като ни изпратите допълнителна информация.
8 | Всичко, което трябва да направите е да изберете \'ОК\' и да ни изпратите протокол за грешката по email.
9 | Всички разрешения са необходими за пълна фунционалност.
10 | Заявка за разрешение
11 | Изпрати
12 | Отказ
13 | Нотификация за затваряне.
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values-bs/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH se srušio
6 | Došlo je do greške zbog čega se APH srušio i zaustavio.\nMolimo vas da nam pomognete da to popravimo tako što ćete nam poslati podatke o pogrešci, sve što trebate je dodirnuti \'U redu\' i poslati ovaj izvještaj e-poštom.
7 | Sve dozvole su potrebne za punu funkcionalnost
8 | Zahtjev za dozvolu
9 | Poslati
10 | Otkaži
11 | Obavijest o padu aplikacije
12 | Upozorenje!
13 |
14 |
15 | Nova verzija pronađena
16 | Nije moguće pronaći aplikaciju za otvaranje ove URL veze
17 | Nije moguće raščlaniti ovu vezu
18 |
19 |
20 | Omogući
21 |
22 |
23 | Dodirnite Dozvoli da biste vidjeli i odobrili potrebne dozvole za snimanje poziva
24 |
25 |
26 | Pomoćnik za Snimanje Poziva
27 | Ova usluga se koristi za pristup zvuku poziva. Nijedan zvuk poziva ne može se snimiti kad je onemogućen. Da bi ograničili pristup vašim podacima, aplikacija ograničava promatranje na vlastite događaje i ne zahtijeva nikakve mogućnosti.\n\nNeki telefoni mogu prijaviti da usluga ne radi. Pokušajte ga onemogućiti i ponovo omogućiti ako se to dogodi.
28 | Dodirnite ovdje da omogućite Pomoćnika za Snimanje Poziva
29 | Molimo omogućite Pomoćnika za Snimanje Poziva na ekranu Instalirane Usluge Pristupačnosti
30 | Pomoćnik za Snimanje Poziva nije omogućen
31 |
32 |
33 | Preuzimanje
34 | Obavijest
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-cs/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH havaroval
6 | Chyba způsobila pád a zastavení funkce APH.\nPomozte nám opravit tento problém tím, že nám pošlete údaje o chybách. Stačí kliknout na \"OK\" a odeslat tuto zprávu emailem.
7 | Všechna oprávnění jsou vyžadována pro plnou funkčnost
8 | Žádost o oprávnění
9 | Odeslat
10 | Zrušit
11 | Oznámení o pádu
12 | Upozornění!
13 |
14 |
15 | Byla nalezena nová verze
16 | Nelze najít žádnou aplikaci pro otevření tohoto odkazu
17 | Nelze analyzovat tento odkaz
18 |
19 |
20 | Povolit
21 |
22 |
23 | Klepnutím na Povolit zobrazení a udělení požadovaných oprávnění pro nahrávání hovorů
24 |
25 |
26 | Pomocník pro nahrávání hovorů
27 | Tato služba se používá pro přístup ke zvukovému hovoru. Pokud je zakázán, nelze zvuk volání zaznamenat. Pro omezení přístupu k vašim datům aplikace omezuje pozorování na své vlastní události a nevyžaduje žádnou funkci\n\nNěkteré telefony mohou hlásit, že služba nefunguje. Zkuste znovu zakázat a povolit, pokud se tak stane.
28 | Klepnutím sem povolíte pomocníka pro nahrávání hovorů
29 | Prosím povolte pomocníka pro nahrávání hovorů na obrazovce Služby usnadnění přístupu
30 | Pomocník pro nahrávání hovorů není povolen
31 | Oznámení
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values-da/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH er gået ned
6 | Der opstod en fejl, der forårsagede, at APH gik ned og stoppede.\nHjælp os med at ordne dette ved at sende os fejldata, alt hvad du skal gøre er at trykke på \'OK\' og sende denne rapport via e-mail.
7 | Alle tilladelser kræves for fuld funktionalitet
8 | Tilladelsesanmodning
9 | Send
10 | Annuller
11 | Meddelelse om nedbrud
12 | Advarsel!
13 |
14 |
15 | Ny version fundet
16 | Kan ikke finde nogen app til at åbne dette link
17 | Kan ikke fortolke dette link
18 |
19 |
20 | Aktiver
21 |
22 |
23 | Tryk på Tillad at se og give de nødvendige tilladelser til opkaldsoptagelse
24 |
25 |
26 | Hjælp til opkaldsoptager
27 | Denne tjeneste bruges til at få adgang til opkaldslyd. Ingen opkaldslyd kan optages, når den er deaktiveret. For at begrænse adgangen til dine data, begrænser app\'en observation til sine egne begivenheder og anmoder ikke om nogen kapacitet\n\nNogle telefoner kan rapportere tjeneste som ikke fungerer. Prøv at deaktivere og aktivere det igen, hvis dette sker.
28 | Tryk her for at aktivere Call Recording Helper
29 | Aktivér venligst Opkaldsoptagelseshjælper på skærmen Installerede Tilgængelighedstjenester
30 | Opkaldsoptagelseshjælper er ikke aktiveret
31 |
32 |
33 | Download
34 |
35 |
36 | Google Play Butik version ACR Phone er ikke installeret eller skal opdateres. Denne app kan kun bruges sammen med en kompatibel version af ACR Phone
37 | Installer
38 | Skal holdes kørende for opkald optagelse funktionalitet ACR Phone
39 | Denne app skal opdateres for at fungere korrekt
40 | Tilladelse til lydoptagelse
41 | Notifikation
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-de/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH ist abgestürzt
6 | Es gab einen Fehler, der APH zum Absturz brachte und stoppte.\nBitte hilf uns, solche Fehler zu beheben, indem du uns die Fehlerdaten schickst. Alles, was du machen musst ist, auf \'OK\' zu tippen und uns diesen Bericht per E-Mail zu schicken. Danke!
7 | Diese Berechtigungen sind für die volle Funktionalität erforderlich
8 | Berechtigungsanfrage
9 | Senden
10 | Abbrechen
11 | Absturzbenachrichtigung
12 | Achtung!
13 |
14 |
15 | Neue Version gefunden
16 | Keine App zum Öffnen dieses Links gefunden
17 | Link kann nicht verarbeitet werden
18 |
19 |
20 | Aktivieren
21 |
22 |
23 | Tippe auf Erlauben, um den erforderlichen Berechtigungen für die Anrufaufzeichnung zuzustimmen
24 |
25 |
26 | Anrufeaufzeichnungsdienst
27 | Dieser Dienst wird zum Aufzeichnen von Anrufen verwendet. Wenn der Dienst deaktiviert ist, können keine Anrufe aufgezeichnet werden. Der Dienst bezieht sich nur auf die App und greift nicht auf andere Daten zu\n\nWenn dein Telefon den Dienst als nicht funktionierend anzeigt, versuche ihn zu deaktivieren und anschließend wieder zu aktivieren.
28 | Hier tippen, um den Anrufeaufzeichnungsdienst zu aktivieren
29 | Bitte aktiviere den Anrufeaufzeichnungsdienst bei den Bedienungshilfen.
30 | Der Anrufeaufzeichnungsdienst ist nicht aktiviert
31 |
32 |
33 | Herunterladen
34 |
35 |
36 | Die Google Play Store Version von ACR Phone ist nicht installiert oder muss aktualisiert werden. Diese App kann nur zusammen mit einer kompatiblen Version von ACR Phone verwendet werden
37 | Installieren
38 | Muss weiterhin aktiv sein, damit ACR Phone Anrufe aufzeichnen kann
39 | Diese App muss aktualisiert werden, um korrekt zu funktionieren
40 | Audio-Aufnahme-Berechtigung
41 | Benachrichtigung
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-el/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Το APH κατέρρευσε
6 | Παρουσιάστηκε σφάλμα προκαλώντας κατάρρευση και διακοπή λειτουργίας του APH.\nΠαρακαλώ βοηθήστε μας να διορθώσουμε αυτό στέλνοντας μας δεδομένα σφάλματος, το μόνο που πρέπει να κάνετε είναι να πατήσετε \'ΟΚ\' και να στείλετε αυτή την αναφορά μέσω email.
7 | Όλα τα δικαιώματα απαιτούνται για την πλήρη λειτουργικότητα
8 | Απαιτείται Άδεια
9 | Αποστολή
10 | Ακύρωση
11 | Crash notification
12 | Προειδοποίηση!
13 |
14 |
15 | Βρέθηκε νέα έκδοση
16 | Δεν είναι δυνατή η εύρεση εφαρμογής για το άνοιγμα αυτού του συνδέσμου
17 | Δεν είναι δυνατή η ανάλυση αυτού του συνδέσμου
18 |
19 |
20 | Ενεργοποίηση
21 |
22 |
23 | Πατήστε το κουμπί Επιτρέπω για να δείτε και να παραχωρήσετε τις απαιτούμενες άδειες για καταγραφή κλήσεων
24 |
25 |
26 | Βοηθός καταγραφής κλήσεων
27 | Αυτή η υπηρεσία χρησιμοποιείται για πρόσβαση στον ήχο κλήσεων. Δεν είναι δυνατή η εγγραφή ήχου κλήσης όταν είναι απενεργοποιημένη. Για να περιορίσετε την πρόσβαση στα δεδομένα σας, η εφαρμογή περιορίζει την παρατήρηση στα δικά της συμβάντα και δεν απαιτεί καμία δυνατότητα \n\nΟρισμένα τηλέφωνα ενδέχεται να αναφέρουν ότι η υπηρεσία δεν λειτουργεί. Δοκιμάστε να το απενεργοποιήσετε και να το ενεργοποιήσετε ξανά εάν συμβεί αυτό.
28 | Πατήστε εδώ για να ενεργοποιήσετε το Βοηθό καταγραφής κλήσεων
29 | Ενεργοποιήστε το Βοηθό καταγραφής κλήσεων στην οθόνη Υπηρεσίες εγκατεστημένης προσβασιμότητας
30 | Ο βοηθός καταγραφής κλήσεων δεν είναι ενεργοποιημένος
31 |
32 |
33 | Λήψη
34 | Γνωστοποίηση
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-et/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH jooksis kokku
6 | APH-i kokkujooksmise ja seiskamise tõttu ilmnes tõrge. \nPalun aidake meil see parandada, saates meile tõrkeandmed. Teil tuleb vaid toksata \"OK\" ja saata see aruanne e-kirjaga.
7 | Kõigi funktsioonide kasutamiseks on vaja kõiki õigusi
8 | Luba nõutud
9 | Saada
10 | Katkesta
11 | Kokkujooksmise teade
12 | Hoiatus!
13 |
14 |
15 | Leitud uus versioon
16 | Selle lingi avamiseks ei leia ühtegi rakendust
17 | Seda linki ei saa töödelda
18 |
19 |
20 | Luba
21 |
22 |
23 | Puudutage valikut \"Luba\", et näha ja anda kõne salvestamiseks vajalikke õigusi
24 |
25 |
26 | Kõnesalvesti abimees
27 | Seda teenust kasutatakse kõne helile juurdepääsu saamiseks. Keelatud kõne heli ei saa salvestada kui see on keelatud. Teie andmetele juurdepääsu piiramiseks piirdub rakendus vaatlemise oma sündmustega\n\nMõned telefonid võivad teatada, et teenus ei tööta. Kui see juhtub, proovige see uuesti keelata ja lubada.
28 | Kõne salvestamise abistaja lubamiseks puudutage siin
29 | Lubage kõne salvestamise abimees installitud juurdepääsetavusteenuste ekraanil
30 | Kõnesalvestamise abimees pole lubatud
31 |
32 |
33 | Lae alla
34 | Teade
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-fa/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH خراب شد
6 | هنگام خرابی و توقف APH خطایی روی داد.\nلطفاً با ارسال اطلاعات خطا برای رفع این مشکل به ما کمک کنید ، فقط کافی است روی \"تأیید\" ضربه بزنید و این گزارش را از طریق ایمیل ارسال کنید.
7 | برای عملکرد کامل ، همه مجوزها لازم است
8 | درخواست مجوز
9 | ارسال
10 | لغو
11 | اعلان خرابی
12 | هشدار
13 |
14 |
15 | نسخه جدید پیدا شد
16 | برای باز کردن این پیوند نمی توانید هیچ برنامه ای پیدا کنید
17 | تجزیه این پیوند امکان پذیر نیست
18 |
19 |
20 | فعال کردن
21 |
22 |
23 | برای مشاهده و اعطای مجوزهای لازم برای ضبط تماس ، روی «مجاز» ضربه بزنید
24 |
25 |
26 | مکالمه ضبط تماس
27 | این سرویس برای دسترسی به صدای تماس استفاده می شود. وقتی صدا غیرفعال باشد ، هیچ فراخوانی ضبط نمی شود. برای محدود کردن دسترسی به داده های شما ، برنامه مشاهده به وقایع خاص خود را محدود می کند و هیچ گونه قابلیتی نیاز نیست. ممکن است برخی از تلفن ها سرویس را گزارش دهند که کار نمی کند. اگر این اتفاق افتاد ، دوباره آن را غیرفعال و فعال کنید.
28 | برای فعال کردن راهنمای ضبط تماس ، روی اینجا ضربه بزنید
29 | لطفاً راهنمای ضبط تماس را در صفحه خدمات دسترسی قابل نصب نصب کنید
30 | راهنمای ضبط تماس فعال نیست
31 | اطلاع
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values-gl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH fallou
6 | Produciuse un erro que causou que APH fallase e se detivese.\nPor favor, axúdenos a solucionalo enviándonos os datos do erro, só ten que premer \"Si\" e enviar este informe por correo electrónico.
7 | Son necesarios todos os permisos para obter funcionalidade completa
8 | Solicitude de permiso
9 | Enviar
10 | Cancelar
11 | Notificación de fallo
12 | Aviso!
13 |
14 |
15 | Atopouse unha nova versión
16 | Non se atopou ningunha aplicación para abrir esta ligazón
17 | Non é posible analizar esta ligazón
18 |
19 |
20 | Activar
21 |
22 |
23 | Toque \"Permitir\" para ver e conceder os permisos necesarios para a gravación de chamadas
24 |
25 |
26 | Asistente de Gravación de Chamadas
27 | Este servizo úsase para acceder ao audio das chamadas. Se está desactivado o audio non poderá ser gravado. Para restrinxir o acceso aos seus datos, a aplicación limítase a observar os seus propios eventos e non solicita ningunha competencia.\n\nPode que algúns teléfonos indiquen que o servizo non funciona. Se isto pasa, intente desactivando e reactivando o servizo.
28 | Tocar aquí para activar o Asistente de Gravación de Chamadas
29 | Por favor, active o Asistente de Gravación de Chamadas na pantalla de Servizos de Accesibilidade Instalados
30 | O Asistente de Gravación de Chamadas non está activado
31 |
32 |
33 | Descargar
34 |
35 | Notificación
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-hi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH क्रैश हो गया है
6 | क्रैश करने और रोकने के लिए ACR Phone के कारण त्रुटि हुई थी.\nकृपया हमें त्रुटि डेटा भेजकर इसे ठीक करने में मदद करें, इसके लिए आपको \'ओके\' पर टैप करना होगा और ईमेल द्वारा यह रिपोर्ट भेजनी होगी.
7 | पूर्ण कार्यक्षमता के लिए सभी अनुमतियों की आवश्यकता होती है
8 | अनुमति अनुरोध
9 | संदेश
10 | रद्द करें
11 | क्रैश नोटिफिकेशन
12 | चेतावनी!
13 |
14 |
15 | नया संस्करण मिला
16 | इस लिंक को खोलने के लिए कोई ऐप नहीं ढूंढ सकता
17 | इस लिंक को पार्स करने में असमर्थ
18 |
19 |
20 | सक्षम करें
21 |
22 |
23 | कॉल रिकॉर्डिंग के लिए आवश्यक अनुमति देखने और देने की अनुमति पर टैप करें
24 |
25 |
26 | कॉल रिकॉर्डिंग हेल्पर
27 | इस सेवा का उपयोग कॉल ऑडियो तक पहुंचने के लिए किया जाता है. अक्षम होने पर कोई कॉल ऑडियो रिकॉर्ड नहीं किया जा सकता है. आपके डेटा तक पहुंच को सीमित करने के लिए, ऐप अपने स्वयं के ईवेंट का अवलोकन करता है और किसी भी क्षमता का अनुरोध नहीं करता है\n कुछ फोन सेवा को काम नहीं करने के रूप में रिपोर्ट कर सकते हैं. ऐसा होने पर इसे फिर से अक्षम और सक्षम करने का प्रयास करें.
28 | कॉल रिकॉर्डिंग हेल्पर सक्षम करने के लिए यहां टैप करें
29 | स्थापित एक्सेसिबिलिटी सर्विसेज स्क्रीन पर कॉल रिकॉर्डिंग हेल्पर सक्षम करें
30 | कॉल रिकॉर्डिंग हेल्पर सक्षम नहीं है
31 |
32 | अधिसूचना
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/values-hr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH se srušio
6 | Došlo je do greške zbog čega se APH srušio i zaustavio.\nMolimo vas da nam pomognete da to popravimo tako što ćete nam poslati podatke o pogrešci, sve što trebate je dodirnuti \'U redu\' i poslati ovaj izvještaj e-poštom.
7 | Sve dozvole su potrebne za punu funkcionalnost
8 | Zahtjev za dozvolu
9 | Poslati
10 | Otkaži
11 | Obavijest o padu aplikacije
12 | Upozorenje!
13 |
14 |
15 | Nova verzija pronađena
16 | Nije moguće pronaći aplikaciju za otvaranje ove URL veze
17 | Nije moguće raščlaniti ovu vezu
18 |
19 |
20 | Omogući
21 |
22 |
23 | Dodirnite Dozvoli da biste vidjeli i odobrili potrebne dozvole za snimanje poziva
24 |
25 |
26 | Pomoćnik za Snimanje Poziva
27 | Ova usluga se koristi za pristup zvuku poziva. Nijedan zvuk poziva ne može se snimiti kad je onemogućen. Da bi ograničili pristup vašim podacima, aplikacija ograničava promatranje na vlastite događaje i ne zahtijeva nikakve mogućnosti.\n\nNeki telefoni mogu prijaviti da usluga ne radi. Pokušajte ga onemogućiti i ponovo omogućiti ako se to dogodi.
28 | Dodirnite ovdje da omogućite Pomoćnika za Snimanje Poziva
29 | Molimo omogućite Pomoćnika za Snimanje Poziva na zaslonu Instalirane Usluge Pristupačnosti
30 | Pomoćnik za Snimanje Poziva nije omogućen
31 |
32 |
33 | Preuzimanje
34 | Obavijest
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-hu/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Az APH összeomlott
6 | Egy hiba miatt az APH összeomlott.\nKérjük segítsen a hibajavításban, riport küldésével! Csak nyomjon az OK gombra a riport emailben való küldéséhez!
7 | Minden engedély megadására szükség van a működéshez
8 | Engedély kérés
9 | Küldés
10 | Mégse
11 | Összeomlási jelzés
12 | Figyelem!
13 |
14 |
15 | Új verzió található
16 | Nem található alkalmazás a link megnyitásához
17 | Nem sikerült értelmezni a linket
18 |
19 |
20 | Engedélyez
21 |
22 |
23 | Érintse meg az Engedélyezés gombot a hívásrögzítéshez szükséges engedélyek megtekintéséhez és megadásához
24 |
25 |
26 | Call Recording Helper
27 | Ez a szolgáltatás a híváshang elérésére szolgál. A hívás hangja nem rögzíthető, ha ez le van tiltva. Az adatokhoz való hozzáférés korlátozása érdekében az alkalmazás saját eseményeire korlátozza a megfigyelést, és nem kér semmilyen lehetőséget.\n\nEgyes telefonok jelezhetik, hogy a szolgáltatás nem működik. Ha ez történik, próbálja meg letiltani, majd újra engedélyezni a szolgáltatást.
28 | Koppintson ide a Call Recording Helper engedélyezéséhez
29 | Engedélyezze a Call Recording Helper funkciót a Telepített kisegítő lehetőségeknél
30 | A Call Recording Helper nincs engedélyezve
31 |
32 |
33 | Letöltés
34 |
35 |
36 | Telepítés
37 | Értesítés
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/values-hy/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH-ը վթարվեց
6 | Վրեպի պատճառով APH-ը վթարվեց և փակվեց։\nԽնդրում ենք, որ մեզ օգնեք, որ խնդիրը կարգավորենք։ Սեղմեք «Լավ» և ուղարկեք վրեպի զեկույցը էլ. փոստով։
7 | Բոլոր թույլտվությունները անհրաժեշտ են ամբողջական ֆունկցիոնալության համար
8 | Թույլտվության Հարցում
9 | Ուղարկել
10 | Չեղարկել
11 | Վթարի ծանուցում
12 | Ուշադրություն.
13 |
14 |
15 | Հասանելի է նոր տարբերակ
16 | Չհաջողվեց գտնել հավելված այս հղումը բացելու համար
17 | Հղումը չի ճանաչվում
18 |
19 |
20 | Միացնել
21 |
22 |
23 | Հպեք «Թույլատրել» կոճգամը, որպեսզի տեսնեք և տաք ձայնագրմանը անհրաժեշտ թույլտվությունները
24 |
25 |
26 | Զանգերի Ձայնագրման Օգնական
27 | Հպեք, որպեսզի պատրաստվել զանգերի ձայնագրմանը
28 | Միացրեք «Զանգերի Ձայնագրման Օգնականը» Տեղադրված Մատչելիության Ծառայություններ էկրանում
29 | Զանգերի Ձայնագրման Օգնականը միացված չէ
30 |
31 |
32 | Ներբեռնել
33 | Ծանուցում
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values-in/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH crash
6 | Ada kesalahan yang membuat APH crash dan berhenti.\nHarap bantu kami membenahinya dengan mengirimkan data kesalahan, yang perlu anda lakukan hanyalah menyentuh \'OK\' dan mengirim laporan ini melalui email.
7 | Semua perizinan diperlukan untuk fungsionalitas penuh
8 | Permintaan Perizinan
9 | Kirim
10 | Batal
11 | Pemberitahuan crash
12 | Perhatian!
13 |
14 |
15 | Versi baru ditemukan
16 | Tidak dapat menemukan apl untuk membuka tautan ini
17 | Tidak dapat mengurai tautan ini
18 |
19 |
20 | Aktifkan
21 |
22 |
23 | Ketuk Izinkan untuk melihat dan memberi izin yang dibutuhkan perekam panggilan
24 | Pemberitahuan
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values-iw/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH קרס
6 | היתה שגיאה שגרמה ל-APH לקרוס ולעצור. \n אנא עזרו לנו לתקן זאת על ידי שליחת מידע על השגיאה, כל אשר עליך לעשות זה ללחוץ על ה-\'OK\' ולשלוח את הדו\"ח במייל.
7 | כל ההרשאות חיוניות עבור ביצוע מלא
8 | בקשת הרשאה
9 | שלח/י
10 | בטל/י
11 | התראת קריסה
12 | אזהרה!
13 |
14 |
15 | נמצאה גירסה חדשה
16 | לא נמצא יישום מתאים לפתיחת קישור זה
17 | לא ניתן לנתח את הקישור הזה
18 |
19 |
20 | לְאַפשֵׁר
21 |
22 |
23 | הקש על אישור כדי לראות ולהעניק הרשאות נדרשות להקלטת שיחות
24 |
25 |
26 | עוזר הקלטת שיחות
27 | שירות זה משמש לגישה לאודיו של שיחה. לא ניתן להקליט אודיו של שיחה כשהוא מושבת. על מנת להגביל את הגישה לנתונים שלך, האפליקציה מגבילה את התצפית לאירועים שלה ואינה מבקשת יכולת כלשהי. טלפונים מסוימים עשויים לדווח כי השירות אינו פועל. נסה להשבית ולהפעיל אותו שוב אם זה קורה.
28 | הקש כאן כדי להפעיל עוזר להקלטת שיחות
29 | אנא הפעל עוזר להקלטת שיחות במסך שירותי נגישות מותקנים
30 | עוזר הקלטת שיחות אינו מופעל
31 |
32 |
33 | הורד
34 | התראות
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ja/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APHがクラッシュしました
6 | APHのクラッシュおよび停止の原因となるエラーがありました。\nエラーデータを送信してこの修正をお手伝いください。「OK」をタップしてこのレポートをメールで送信するだけです。
7 | 全機能を使うには、すべての許可が必要です
8 | 許可リクエスト
9 | 送信
10 | キャンセル
11 | クラッシュ通知
12 | 警告!
13 |
14 |
15 | 新しいバージョンが見つかりました
16 | このリンクを開くアプリが見つかりません
17 | このリンクを解析できません
18 |
19 |
20 | 有効にする
21 |
22 |
23 | [許可] をタップして、通話録音に必要な許可を表示して付与します
24 |
25 |
26 | 通話録音ヘルパーに電話する
27 | このサービスは、通話音声へのアクセスに使われます。無効にすると、通話音声を録音できません。データへのアクセスを制限するため、アプリは監視を独自のイベントに制限し、機能を要求しません。\n\n一部の電話のサービスが機能していないと報告する場合があります。これが発生した場合、一旦無効後再度有効にしてみてください。
28 | 通話録音ヘルパーを有効にするには、ここをタップしてください
29 | インストールされているユーザー補助サービス画面で通話録音ヘルパーを有効にしてください
30 | 通話録音ヘルパーが有効になっていません
31 |
32 |
33 | ダウンロード
34 | お知らせ
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ko/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH 작동이 중단되었습니다
6 | APH을 작동 중지시키는 오류가 발생했습니다.\n이 문제를 해결할 수 있도록 오류 데이터를 보내주세요. \'확인\'을 탭하고 보고서를 이메일로 보내기만 하면됩니다.
7 | 전체 기능을 사용하려면 모든 권한이 필요합니다
8 | 허가 요청
9 | 전송
10 | 취소
11 | 작동 중지 알림
12 | 경고!
13 |
14 |
15 | 새 버전 발견
16 | 이 링크를 열 수 있는 앱을 찾을 수 없습니다
17 | 이 링크를 구문 분석할 수 없습니다.
18 |
19 |
20 | 활성화
21 |
22 |
23 | 허용을 탭하여 통화 녹음에 필요한 권한을 확인하고 부여하십시오
24 |
25 |
26 | 통화 녹음 도우미
27 | 이 서비스는 통화 오디오에 액세스하는데 사용됩니다. 비활성화되면 통화 오디오를 녹음 할 수 없습니다. 데이터에 대한 액세스를 제한하기 위해 앱은 자체 이벤트에 대한 관찰을 제한하고 기능을 요청하지 않습니다\n\n일부 휴대폰은 서비스가 작동하지 않는 것으로 보고될 수 있습니다. 이 경우 비활성화했다가 다시 활성화하십시오.
28 | 통화 녹음 도우미를 활성화하려면 여기를 누르십시오
29 | 설치된 접근성 서비스 화면에서 통화 녹음 도우미를 활성화하십시오
30 | 통화 녹음 도우미가 활성화되지 않았습니다
31 |
32 |
33 | 내려받기
34 | 알림
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ku/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | مۆبایلی ئەی سی ئاڕ تێکشکا
6 | هەڵەیەک ڕوویدا کە وای لە APH کرد کە تێکبدا و بوەستێ.\nتکایە یارمەتیمان بدە بۆ چارەسەرکردنی ئەم زانیاریە بە ناردنی داتای هەڵە بۆ ئێمە، هەموو ئەوەی کە دەبێت نەرم لێبدەی لە \'OK\' و ئەم ڕاپۆرتە بنێرە بە ئیمەیڵ
7 | هەموو مۆڵەتەکان پێویستە بۆ کرداری تەواو
8 | داواکردنی مۆڵەت پێدان
9 | ناردن
10 | لابردن
11 | ئاگانامەی پێکدادان
12 | ئاگادارکردنهوه !
13 |
14 |
15 | وەشانی نوێ دۆزرایەوە: %1$s
16 | هیچ ئەپێک نەدۆزرایەوە بۆ کردنەوەی ئەم پەڕگەیە
17 | ناتوانێت ئەم لینکە شیبکاتەوە
18 |
19 |
20 | چالاککردن
21 |
22 |
23 | نەرم لێبدە لەسەر ڕێپێدان بۆ بینین و پێدانی ڕێپێدانە پێویستەکان بۆ تۆمارکردنی پەیوەندی
24 |
25 |
26 | پەیوەندی کردن تۆمارکردن یارمەتیدەر
27 | ئەم خزمەتگوزاریە بەکاردێت بۆ گەیشتن بە دەنگی پەیوەندی. هیچ دەنگی پەیوەندیکردن ێک ناتوانێت تۆمار بکرێت کاتێک ناچالاک کراوە. بۆ سنووردارکردنی دەستگەیشتن بە داتاکەت، کاربەرنامە چاودێری ڕووداوەکانی خۆی سنووردار دەکات و داوای هیچ توانایەک ناکات\n\nSome لەوانەیە خزمەتگوزاری ڕاپۆڕت بکات وەک کارناکات. هەوڵدە لەکار کەیت و دووبارە چالاک بکە ئەگەر ئەمە ڕوویدا
28 | نەرم لێبدە لێرە بۆ بەتواناکردنی یارمەتیدەری تۆمارکردنی پەیوەندی
29 | تکایە یارمەتیدەری تۆمارکردنی پەیوەندی چالاک بکە لەسەر شاشەی خزمەتگوزاریە دامەزراوەکان
30 | یارمەتیدەری تۆمارکردنی پەیوەندی چالاک نەکراوە
31 |
32 |
33 | دابەزاندن
34 | ئاگادارکردنەوە
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values-lt/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH užlūžo
6 | Įvyko klaida, dėl kurios APH užlūžo ir nustojo veikti.\nPadėkite mums ateityje išvengti panašių klaidų, viskas, ką turėtumėte padaryti, tau spustelėti \'OK\' ir nusiųsti šią ataskaitą el. paštu.
7 | Pilnam funkcionavimui reikalingi visi leidimai
8 | Leidimo užklausa
9 | Siųsti
10 | Atšaukti
11 | Pranešimas apie klaidą
12 | Įspėjimas!
13 |
14 |
15 | Rasta nauja versija
16 | Nerasta programa nuorodai atidaryti
17 | Klaida nuskaitant nuorodą
18 |
19 |
20 | Įjungti
21 |
22 |
23 | Spustelkite ant Leisti norint pamatyti bei suteikti reikalingus leidimus skambučių įrašymui
24 |
25 |
26 | Skambučio Įrašymo Pagalbininkas
27 |
28 | Priminimas
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ms/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH telah terhenti
6 | Terdapat kegagalan yang menyebabkan APH terhenti.\nBantu kami untuk baiki kelemahan ini dengan menghantar data kegagalan, hanya tekan \'OK\' untuk menghantar laporan melalui email.
7 | Semua kebenaran diperlukan untuk fungsi penuh
8 | Meminta Kebenaran
9 | Hantar
10 | Batal
11 | Notifikasi Kegagalan
12 | Amaran!
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values-nb-rNO/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH har krasjet
6 | Det oppstod en feil som fikk APH til å krasje og stoppe.\nHjelp oss med å fikse dette ved å sende oss feildata. Alt du trenger å gjøre er å trykke på \'OK\' og sende rapporten via e-post.
7 | Alle tillatelser er påkrevd for full funksjonalitet
8 | Forespørsel om tillatelse
9 | Send
10 | Avbryt
11 | Krasj varsler
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-nl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH is gecrasht
6 | Er is een fout opgetreden waardoor APH crashte en stopte.\nHelp ons dit op te lossen door ons foutgegevens te sturen, je hoeft alleen maar op \'OK\' te tikken en dit rapport per e-mail te verzenden.
7 | Alle bevoegdheden zijn vereist voor volledige functionaliteit
8 | Machtigingsverzoek
9 | Verzenden
10 | Annuleren
11 | Crash melding
12 | Waarschuwing!
13 |
14 |
15 | Nieuwe versie gevonden
16 | Kan geen enkele app vinden om deze link te openen
17 | Kan deze link niet parsen
18 |
19 |
20 | Inschakelen
21 |
22 |
23 | Tik op Toestaan om de vereiste machtigingen voor oproepopname te bekijken en te geven
24 |
25 |
26 | Oproep Opname Helper
27 | Deze service wordt gebruikt om toegang te krijgen tot de audio van een telefoongesprek. Er kan geen audio worden opgenomen wanneer deze uitgeschakeld is. Om de toegang tot uw gegevens te beperken, beperkt de app waarnemingen tot zijn eigen gebeurtenissen\n\nSommige telefoons kunnen aangeven dat ze niet werken. Probeer dit uit te schakelen en weer in te schakelen als dit gebeurt.
28 | Tik hier om de Oproep Opname Helper in te schakelen
29 | Schakel de Oproep Opname Helper in op Geïnstalleerde Toegankelijkheid Services scherm
30 | Oproep Opname Helper is niet ingeschakeld
31 |
32 |
33 | Download
34 |
35 |
36 | Google Play Store versie ACR Phone is niet geïnstalleerd of moet worden bijgewerkt. Deze app kan alleen samen met een compatibele versie van de ACR Phone gebruikt worden
37 | Installeren
38 | Moet actief blijven voor gespreksopnamefunctie van ACR Phone
39 | Deze app moet worden bijgewerkt om correct te kunnen functioneren
40 | Audio-opname rechten
41 | Melding
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-no-rNO/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH har krasjet
6 | Det oppstod en feil som fikk APH til å krasje og stoppe.\nHjelp oss med å fikse dette ved å sende oss feildata. Alt du trenger å gjøre er å trykke på \'OK\' og sende rapporten via e-post.
7 | Alle tillatelser er påkrevd for full funksjonalitet
8 | Forespørsel om tillatelse
9 | Send
10 | Avbryt
11 | Krasj varsler
12 | Advarsel!
13 |
14 |
15 | Ny versjon funnet
16 | Finner ikke noen app for å åpne denne lenken
17 | Kan ikke åpne denne koblingen
18 |
19 |
20 | Aktiver
21 |
22 |
23 | Trykk på tillat for å se og gi nødvendige tillatelser for opptak av anrop
24 |
25 |
26 | Hjelp for samtaleopptak
27 | Denne tjenesten brukes for å få tilgang til lyd fra samtaler. Ingen samtaler kan tas opp hvis den er deaktivert. For å begrense tilgangen til dine data, ser ACR app kun egne hendelser og ber ikke om utvidet tilgang\n\nNoen telefoner kan rapportere tjenester som ikke fungerende. Prøv å deaktivere og aktivere det igjen hvis dette skjer.
28 | Trykk her for å aktivere samtaleopptakshjelper
29 | Vennligst aktiver samtaleopptakshjelper på skjermen for tilgjengelighetstjenester
30 | Samtaleopptakhjelper er ikke aktivert
31 |
32 |
33 | Last ned
34 | Varsling
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pl/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wystąpił błąd z aplikacją APH
6 | Wystąpił błąd z aplikacją APH i została zatrzymana. \nPomóż nam naprawić ten błąd wysyłając nam raport o błędach, kliknij \"OK\" aby wysłać ten raport pocztą elektroniczną.
7 | Wszystkie uprawnienia są wymagane aby aplikacja działała poprawnie
8 | Uprawnienia
9 | Wyślij
10 | Anuluj
11 | Powiadomienie o awariach
12 | Ostrzeżenie!
13 |
14 |
15 | Znaleziono nową wersję
16 | Nie można znaleźć żadnej aplikacji do otwarcia tego linku
17 | Nie można otworzyć linku
18 |
19 |
20 | Włącz
21 |
22 |
23 | Zezwól na uprawnienia do wyświetlania i nagrywania połączeń
24 |
25 |
26 | Pomocnik nagrywania połączeń
27 | Ta usługa jest używana do dostępu do dźwięku połączenia. Żadne połączenie nie może być nagrywane, gdy jest wyłączone. W celu ograniczenia dostępu do Twoich danych, aplikacja ogranicza obserwację do własnych wydarzeń i nie wymaga żadnych możliwości\n\nNiektóre telefony mogą zgłaszać usługę jako niedziałającą. Spróbuj wyłączyć i włączyć ponownie, jeśli tak się stanie.
28 | Dotknij tutaj, aby włączyć pomocnika nagrywania połączeń
29 | Proszę włączyć pomocnika nagrywania połączeń na ekranie Usług Dostępności
30 | Pomocnik nagrywania połączeń nie jest włączony
31 |
32 |
33 | Pobierz
34 |
35 |
36 | Aplikacja ACR Phone w sklepie Google Play nie jest zainstalowana lub musi zostać zaktualizowana. Ta aplikacja może być używana tylko razem z kompatybilną wersją telefonu ACR
37 | Zainstaluj
38 | Musi być uruchomiony dla funkcji nagrywania połączeń ACR Phone
39 | Ta aplikacja musi być zaktualizowana aby działać poprawnie
40 | Uprawnienia do nagrywania dźwięku
41 | Powiadomienie
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pt-rBR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH travou
6 | Houve um erro que originou um bloqueio da aplicação APH.\nPor favor ajude-nos a corrigir este problema enviando-nos dados do erro, basta clicar em \'OK\' e enviar este relatório por e-mail.
7 | Todas as permissões são necessárias para todas as funcionalidades
8 | Solicitação de permissão
9 | Enviar
10 | Cancelar
11 | Notificação de falha
12 | Atenção!
13 |
14 |
15 | Nova versão encontrada
16 | Não foi possível encontrar um app para abrir este link
17 | Não foi possível analisar este link
18 |
19 |
20 | Ativar
21 |
22 |
23 | Toque em Permitir para ver e conceder as permissões necessárias para a gravação de chamadas
24 |
25 |
26 | Assistente de Gravação
27 | Este serviço é usado para acessar o áudio de chamadas. Nenhum áudio de chamada pode ser gravado quando está desativado. Para limitar o acesso aos seus dados, o aplicativo limita a observação em seus próprios eventos e não demanda nenhum recurso\n\nAlguns telefones podem relatar como não funcionando. Tente desativar e ativar novamente se isto acontecer.
28 | Toque aqui para ativar o Assistente de Gravação de Chamadas
29 | Por favor, ative o Assistente de Gravação de Chamadas na tela de Serviços de Acessibilidade Instalados
30 | O Assistente de Gravação de Chamadas não está ativado
31 |
32 |
33 | Baixar
34 |
35 |
36 | A versão ACR Phone do Google Play Store não está instalada ou precisa ser atualizada. Este app só pode ser usado juntamente com uma versão compatível do ACR Phone
37 | Instalar
38 | Serviço para manter a funcionalidade de gravação de chamadas do ACR Phone
39 | Este aplicativo deve ser atualizado para funcionar corretamente
40 | Permissão de gravação de áudio
41 | Notificação
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ro/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH s-a oprit
6 | A apărut o eroare ce a dus la blocarea și oprirea APH.\nVă rugăm să ne ajutați să o rezolvăm, trimițându-ne date de eroare, tot ce trebuie să faceți fiind să apăsați „OK“ și să trimiteți acest raport prin e-mail.
7 | Toate permisiunile sunt necesare pentru funcționalitate completă
8 | Solicitare de permisiune
9 | Trimiteți
10 | Anulare
11 | Notificare de eroare
12 | Atenție!
13 |
14 |
15 | Permite
16 |
17 |
18 | Apăsați pe Permiteți pentru a vedea și acorda permisiunile necesare pentru înregistrarea apelurilor
19 |
20 |
21 | Ajutor pentru înregistrarea apelurilor
22 | Acest serviciu este utilizat pentru accesarea sunetului apelului. Niciun sunet de apel nu poate fi înregistrat când este dezactivat. Pentru a limita accesul la datele dvs., aplicația limitează observarea la propriile evenimente și nu solicită nicio capacitate. Unele telefoane pot raporta că serviciul nu funcționează. Încercați să o dezactivați și să o activați din nou dacă se întâmplă acest lucru.
23 | Atingeți aici pentru a activa asistentul pentru înregistrarea apelurilor
24 | Vă rugăm să activați ajutorul pentru înregistrarea apelurilor pe ecranul Servicii de accesibilitate instalate
25 | Asistentul pentru înregistrarea apelurilor nu este activat
26 |
27 |
28 | Descarcare
29 | Notificare
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ru/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Программа APH завершена с ошибкой
6 | Произошла ошибка при сбое и остановке работы APH.\nПожалуйста, помогите нам исправить это, отправив нам данные об ошибке. Все что вам надо сделать чтобы отправить этот отчет по электронной почте, только нажать \'OK\'.
7 | Все права необходимы для полной функциональности
8 | Запрос разрешения
9 | Отправить
10 | Отменить
11 | Уведомление об ошибке
12 | Внимание!
13 |
14 |
15 | Найдена новая версия
16 | Не удается найти приложение для открытия ссылки
17 | Не удалось определить ссылку
18 |
19 |
20 | Включить
21 |
22 |
23 | Нажать на кнопку Разрешения для просмотра и предоставления необходимых разрешений для записи звонка
24 |
25 |
26 | Помощник записи вызовов
27 | Этот сервис используется для доступа к звуку вызовов. Звук звонков не может быть записан, если он отключен. Чтобы ограничить доступ к вашим данным, приложение ограничивает наблюдение за собственными событиями и не запрашивает никаких возможностей\n\nНекоторые телефоны могут сообщать об ошибках в работе службы. Попробуйте отключить и снова включить его, если это произойдет.
28 | Нажмите здесь, чтобы включить поддержку записи вызовов
29 | Пожалуйста, включите \"Помощник записи звонков\" на экране \"Услуги специальных возможностей\"
30 | Помощник записи вызовов не включен
31 |
32 |
33 | Загрузить
34 |
35 |
36 | Версия ACR телефона Google Play Store не установлена или должна быть обновлена. Это приложение можно использовать только вместе с совместимой версией ACR Phone
37 | Установить
38 | Выполняется для работы функции записи вызовов ACR телефона
39 | Приложение должно быть обновлено для правильной работы
40 | Права на запись аудио
41 | Уведомление
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-si/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ඒසීආර් පෝන් බිඳ වැටී ඇත
6 | ඒසීආර් පෝන් බිඳ වැටී හා නතර වීමට හේතුවන දෝෂයක් ඇති විය.\nඅපට දෝෂ දත්ත එවීම මගින් මෙය නිවැරදි කිරීමට උදව් කරන්න, ඔබ කළ යුතු දේ නම් \'හරි\' මත තට්ටු කර වි-තැපෑලෙන් මෙම වාර්තාව එවීමයි.
7 | පූර්ණ ක්රියාකාරිත්වයට සියළු අවසර ඇවැසිය
8 | අවසරය ඉල්ලීම
9 | යවන්න
10 | අවලංගු
11 | බිඳවැටීමේ දැනුම්දීම
12 | අවවාදයයි!
13 |
14 |
15 | නව අනුවාදයක් හමු විය
16 | මෙම සබැඳිය විවෘත කිරීමට කිසිදු යෙදුමක් හමු නොවිණි
17 |
18 |
19 | සබල කරන්න
20 |
21 |
22 | ඇමතුම් පටිගත කිරීමට ඇවැසි අවසර දැක ලබා දීමට ඉඩදෙන්න මත තට්ටු කරන්න
23 |
24 |
25 | ඇමතුම් පටිගත උපකාරකය
26 | ඇමතුමේ හඬට ප්රවේශ වීමට මෙම සේවාව භාවිතා කරයි. එය අබල කර ඇති විට ඇමතුමේ හඬ පටිගත කළ නොහැකිය. ඔබගේ දත්ත වෙත ප්රවේශය සීමා කිරීමට, යෙදුම එහිම සිදුවීම් සීමා කරගන්නා අතර කිසිම ශක්යතාවක් ඉල්ලන්නේ නැත\n\nසමහර දුරකථන මෙය වැඩ නොකරන බව වාර්තා කළ හැකිය. මෙය සිදුවන්නේ නම් එය අබල කර යළි සබල කිරීමට උත්සාහ කරන්න.
27 | ඇමතුම් පටිගත උපකාරකය සබල කිරීමට මෙහි තට්ටු කරන්න
28 | ස්ථාපිත ප්රවේශ්යතා සේවා තිරයෙහි ඇමතුම් පටිගත උපකාරකය සබල කරන්න
29 | ඇමතුම් පටිගත උපකාරකය සබල කර නැත
30 |
31 |
32 | බාගන්න
33 |
34 | දැනුම්දීම
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sk/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Aplikácia APH prestala pracovať
6 | Vznikla chyba, ktorá spôsobila, že APH prestala pracovať.\nProsíme pomôžte nám opraviť túto chybu odoslaním dát o chybe. Stačí kliknúť na \'OK\' a odoslať email.
7 | Pre správne fungovanie sú potrebné všetky povolenia
8 | Žiadosť o povolenie
9 | Odoslať
10 | Zrušiť
11 | Upozornenie pri zlyhaní
12 | Upozornenie!
13 |
14 |
15 | Nájdená nová verzia
16 | Na otvorenie tohto odkazu nie je možné nájsť žiadnu aplikáciu
17 | Nie je možné analyzovať tento odkaz
18 |
19 |
20 | Povoliť
21 |
22 |
23 | Kliknutím na povoliť zobrazíte a udelíte požadované povolenie pre nahrávanie hovorov
24 |
25 |
26 | Pomocník pre nahrávanie hovorov
27 | Táto služba sa používa pre prístup k zvukovému hovoru. Pokiaľ je zakázaná, nie je možné zvuk hovoru zaznamenať. Pre omedzenie k prístupu k vašim údajom aplikácia obmedzuje pozorovanie na svoje vlastné udalosti a nevyžaduje žiadnu funkciu\n\nNiektoré telefóny môžu hlásiť, že služba nefunguje. Skúste ju znovu zakázať a povoliť, pokiaľ sa tak stane.
28 | Klepnutím sem povolíte pomocníka pre nahrávanie hovorov
29 | Povoľte prosím pomocníka pre nahrávanie hovorov na obrazovke Služby uľahčenia prístupu
30 | Pomocník pre nahrávanie hovorov nie je povolený
31 |
32 |
33 | Stiahnúť
34 |
35 |
36 | Verzia ACR Phone z obchodu Google Play nie je nainštalovaná, alebo je potrebné ju aktualizovať. Túto aplikáciu je možné používať iba spolu s kompatibilnou verziou ACR Phone.
37 | Inštalovať
38 | Pre funkciu nahrávania hovorov musí byť ACR Phone spustený
39 | Táto aplikácia musí byť aktualizovaná, aby fungovala správne
40 | Povolenie pre nahrávanie zvuku
41 | Oznámenie
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sq/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Kërkesë për Leje
6 | Dërgo
7 | Anullo
8 | Njoftime për avari
9 | Paralajmërim!
10 |
11 |
12 | Shkarko
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH se srušio
6 | Došlo je do greške zbog čega se APH srušio i zaustavio.\nPomozite nam da to popravimo tako što ćete nam poslati podatke o grešci, potrebno je samo da dodirnete \'OK\' i ovaj izveštaj pošaljete e-poštom.
7 | Sve dozvole su potrebne za punu funkcionalnost
8 | Potrebne dozvole
9 | Pošalji
10 | Otkaži
11 | Obaveštenje o padu aplikacije
12 | Upozorenje!
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sv/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH har kraschat
6 | Det uppstod ett fel så APH kraschade.\nHjälp oss gärna genom att trycka på \"OK\" och skicka in rapporten med felloggen till oss via e-post.
7 | Alla behörigheter krävs för full funktionalitet
8 | Behörighetsbegäran
9 | Skicka
10 | Avbryt
11 | Kraschavisering
12 | Varning!
13 |
14 |
15 | Hittade ny version
16 | Kan inte hitta någon app för att öppna den här länken
17 | Det gick inte att analysera den här länken
18 |
19 |
20 | Aktivera
21 |
22 |
23 | Tryck på Tillåt för att bevilja nödvändiga behörigheter för samtalsinspelning
24 |
25 |
26 | Samtalsinspelningshjälpare
27 | Denna tjänst används för att komma åt samtalsljud. Inget samtalsljud kan spelas in när det är inaktiverat.\n\nVissa telefoner kan rapportera att tjänsten inte fungerar. Prova då att inaktivera och aktivera den igen för att se om det hjälper.
28 | Tryck här för att aktivera samtalsinspelningshjälpare
29 | Aktivera samtalsinspelningshjälparen på tillgänglighetstjänstskärmen
30 | Samtalsinspelningshjälpare är inaktiverad
31 |
32 |
33 | Ladda ner
34 |
35 |
36 | Google Play Butikens version av ACR Phone är inte installerad eller måste uppdateras. Appen kan endast användas tillsammans med en kompatibel version av ACR Phone
37 | Installera
38 | Måste hållas igång för samtalsinspelningsfunktion av ACR Phone ska fungera
39 | Appen måste uppdateras för att fungera korrekt
40 | Behörighet för ljudinspelning
41 | Avisering
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-tr/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH çöktü
6 | Beklenmeyen bir hata APH\'nin çökmesine neden oldu. Lütfen bu hatayı bize bildirerek giderilmesine yardımcı olun
7 | Tam işlevsellik için tüm izinler gereklidir
8 | İzin Talebi
9 | Yolla
10 | İptal
11 | Çökme bildirimi
12 | Uyarı!
13 |
14 |
15 | Yeni versiyon bulundu
16 | Bu bağlantıyı açmak için herhangi bir uygulama bulunamıyor
17 | Bu bağlantı ayrıştırılamıyor
18 |
19 |
20 | Etkinleştir
21 |
22 |
23 | Arama ses kaydı için gerekli izinleri görmek ve vermek için İzin Ver\'e dokunun
24 |
25 |
26 | Arama Kaydı Yardımcısı
27 | Bu hizmet, arama sırasında sese erişmek için kullanılır. Devre dışı bırakıldığında hiçbir arama sesi kaydedilemez. Verilerinize erişimi sınırlandırmak için uygulama, gözlemi kendi etkinlikleriyle sınırlar ve herhangi bir kabiliyet talep etmez\n\nBazı telefonlar hizmetin çalışmadığını bildirebilir. Böyle bir durumda devre dışı bırakıp tekrar etkinleştirmeyi deneyin.
28 | Arama Kaydı Yardımcısı\'nı etkinleştirmek için buraya dokunun
29 | Lütfen Yüklü Erişilebilirlik Hizmetleri ekranında Aram Kaydı Yardımcısı\'nı etkinleştirin
30 | Arama Kaydı Yardımcısı etkin değil
31 |
32 |
33 | İndir
34 |
35 |
36 | ACR Telefon\'un Google Play Store sürümü kurulu değil veya güncellenmesi gerekiyor. Bu uygulama yalnızca uyumlu bir ACR Telefon sürümüyle birlikte kullanılabilir
37 | Install
38 | ACR Phone\'un arama kaydetme işlevi için çalışır durumda tutulmalıdır
39 | Bu uygulamanın düzgün çalışması için güncellenmesi gerekmektedir
40 | Ses kaydı izni
41 | Bildirim
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-uk/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Додаток APH аварійно завершив роботу
6 | Сталася помилка та робота APH була аварійно завершена.\nБудь ласка, допоможіть нам виправити це, надіславши нам інформацію про помилку, для цього вам лише потрібно натиснути \'Гаразд\' та надіслати цей звіт електронною поштою.
7 | Для повної функціональності необхідні всі дозволи
8 | Запит на дозвіл
9 | Надіслати
10 | Скасувати
11 | Сповіщення про аварію
12 | Попередження!
13 |
14 |
15 | Знайдено нову версію
16 | Не вдається знайти програму, щоб відкрити це посилання
17 | Не вдалося обробити це посилання
18 |
19 |
20 | Увімкнути
21 |
22 |
23 | Натисніть на кнопку \"Дозволити\" і надайте дозвіл для запису дзвінків
24 |
25 | Сповіщення
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/values-uz/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH nosozlik yuz berdi
6 | Ruxsat so‘rovi
7 | Yuborish
8 | Bekor qilish
9 | Diqqat!
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values-vi/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH đã bị sự cố
6 | Đã xảy ra lỗi khiến APH bị sự cố và dừng.\nVui lòng giúp chúng tôi khắc phục điều này bằng cách gửi cho chúng tôi dữ liệu lỗi, tất cả những gì bạn phải làm là nhấn \'OK\' và gửi báo cáo này qua email.
7 | Tất cả các quyền được yêu cầu cho chức năng đầy đủ
8 | Yêu cầu cấp quyền
9 | Gửi
10 | Huỷ
11 | Thông báo sự cố
12 | Cảnh báo!
13 |
14 |
15 | Có phiên bản mới
16 | Không thể tìm thấy bất kỳ ứng dụng nào để mở liên kết này
17 | Không thể phân tích liên kết này
18 |
19 |
20 | Bật
21 |
22 |
23 | Nhấn vào Cho phép để xem và cấp các quyền cần thiết để ghi âm cuộc gọi
24 |
25 |
26 | Trợ giúp Ghi âm Cuộc gọi
27 | Dịch vụ này được sử dụng để truy cập âm thanh cuộc gọi. Không thể ghi âm cuộc gọi khi nó bị tắt. Để hạn chế quyền truy cập vào dữ liệu của bạn, ứng dụng giới hạn khả năng quan sát đối với các sự kiện riêng và không yêu cầu bất kỳ khả năng nào\n\nMột số điện thoại có thể báo cáo dịch vụ là không hoạt động. Hãy thử tắt và bật lại nếu điều này xảy ra.
28 | Nhấn vào đây để bật Trình trợ giúp ghi âm cuộc gọi
29 | Vui lòng bật Trình trợ giúp ghi âm cuộc gọi trên màn hình Dịch vụ trợ năng đã cài đặt
30 | Trình trợ giúp ghi âm cuộc gọi chưa được bật
31 |
32 |
33 | Tải xuống
34 |
35 |
36 | Phiên bản Cửa hàng Google Play của ACR Điện thoại chưa được cài đặt hoặc phải được cập nhật. Ứng dụng này chỉ có thể được sử dụng cùng với phiên bản ACR Phone tương thích
37 | Cài đặt
38 | Phải tiếp tục chạy cho chức năng ghi âm cuộc gọi của ACR Phone
39 | Ứng dụng này phải được cập nhật để hoạt động chính xác
40 | Quyền ghi âm thanh
41 | Thông báo
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rCN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH 已崩溃
6 | 发生错误,导致 APH 崩溃并停止。请通过向我们发送错误数据来帮助我们解决此问题,您只需点击“确定”并通过电子邮件发送此报告。
7 | 完整功能需要所有权限
8 | 权限请求
9 | 发送
10 | 取消
11 | 崩溃通知
12 | 警告!
13 |
14 |
15 | 发现新版本
16 | 找不到任何应用程序可以打开此链接
17 | 无法解析此链接
18 |
19 |
20 | 启用
21 |
22 |
23 | 点击允许查看并授予所需的通话记录权限
24 |
25 |
26 | 通话记录助手
27 | 此服务用于访问呼叫音频。禁用时,无法录制通话音频。为了限制对您数据的访问,应用程序将观察仅限于其自身的事件,并且不要求任何功能。某些手机可能会报告服务不起作用。如果发生这种情况,请尝试禁用并再次启用它。
28 | 点按此处启用通话记录助手
29 | 请在“已安装的辅助功能”屏幕上启用呼叫记录助手
30 | 呼叫记录助手未启用
31 |
32 |
33 | 下载
34 | 通知
35 | 确定
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rTW/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | APH 已經程式崩潰
6 | APH已出錯導致程序崩潰和停止運作。\n請幫我們回報造成錯誤的資訊,您僅需點擊「OK」按鈕並將報錯資料用電子郵件傳送
7 | 完整的功能需要所有權限
8 | 要求權限
9 | 發送
10 | 取消
11 | 通知崩潰
12 | 警告!
13 |
14 |
15 | 發現新版本
16 | 找不到可開啟此連結的應用程式
17 | 無法解析此連結
18 |
19 |
20 | 啟用
21 |
22 |
23 | 點擊允許以查看並授予所需的通話記錄權限
24 |
25 |
26 | 通話記錄助手
27 | 此服務用於訪問呼叫音頻。禁用時,無法錄製通話音頻。為了限制對您數據的訪問,應用程式將監視僅限於其自身的事件,並且不要求任何功能。某些手機可能會報告服務無法正常工作。如果發生這種情況,請嘗試禁用並再次啟用它。
28 | 點按此處啟用通話記錄助手
29 | 請在“已安裝的輔助功能”屏幕上啟用呼叫記錄幫助器
30 | 呼叫記錄助手未啟用
31 |
32 |
33 | 下載
34 | 通知
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values/app_helper_known_certs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - AB0539F2CC3149FE986EEF9B46525BF117972FF4BA6592749296D21078A552BD
5 |
6 | - 56158AAD73625E513FBFEEEBE1C4F04D7A992822D8C4916A8257EE19B4ACD8B5
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 24dp
4 | 16dp
5 | 8dp
6 | 12dp
7 | 0dp
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/helper_call_recording_accessibility_service.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/nllStore/java/com/nll/helper/StoreConfigImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper
2 |
3 | import android.content.ActivityNotFoundException
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.Uri
7 |
8 | object StoreConfigImpl : IStoreConfig {
9 | override fun openACRPhoneDownloadLink(context: Context, packageName: String) = try {
10 | Intent(
11 | Intent.ACTION_VIEW,
12 | Uri.parse("market://details?id=$packageName")
13 | )
14 | .let(context::startActivity)
15 | true
16 |
17 | } catch (ignored: ActivityNotFoundException) {
18 | try {
19 | Intent(
20 | Intent.ACTION_VIEW,
21 | Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
22 | )
23 | .let(context::startActivity)
24 | true
25 | } catch (e: ActivityNotFoundException) {
26 | e.printStackTrace()
27 | false
28 | }
29 | }
30 |
31 | override fun canLinkToWebSite() = true
32 | override fun canLinkToGooglePlayStore()= true
33 | override fun getUpdateCheckUrl() = "https://acr.app/version-nll-app-store.json"
34 | override fun requiresProminentPrivacyPolicyDisplay() = false
35 | override fun getPrivacyPolicyUrl()= "https://acr.app/policy.htm"
36 | }
--------------------------------------------------------------------------------
/app/src/nllStore/java/com/nll/helper/update/DownloadUrlOpenerImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.widget.Toast
7 | import com.nll.helper.R
8 | import com.nll.helper.recorder.CLog
9 | import com.nll.helper.update.contract.IDownloadUrlOpener
10 | import com.nll.helper.update.version.RemoteAppVersion
11 | import com.nll.helper.util.Util
12 |
13 | object DownloadUrlOpenerImpl : IDownloadUrlOpener {
14 | private const val logTag = "DownloadUrlOpenerImpl"
15 | override fun getOpenDownloadUrlIntent(
16 | context: Context,
17 | remoteAppVersion: RemoteAppVersion
18 | ): Intent {
19 |
20 | val isNllAppStoreInstalled = Util.isAppInstalled(context, "com.nll.store")
21 | val urlToOpenString = if (isNllAppStoreInstalled) {
22 | remoteAppVersion.downloadUrl
23 | } else {
24 | "https://acr.app"
25 | }
26 |
27 | CLog.log(logTag, "getOpenDownloadUrlIntent -> remoteAppVersion: $remoteAppVersion")
28 |
29 | return Intent(Intent.ACTION_VIEW, Uri.parse(urlToOpenString)).apply {
30 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
31 | }
32 | }
33 |
34 | override fun openDownloadUrl(context: Context, remoteAppVersion: RemoteAppVersion) {
35 | val isNllAppStoreInstalled = Util.isAppInstalled(context, "com.nll.store")
36 | val urlToOpenString = if (isNllAppStoreInstalled) {
37 | remoteAppVersion.downloadUrl
38 | } else {
39 | "https://acr.app"
40 | }
41 | CLog.log(logTag, "openDownloadUrl -> remoteAppVersion: $remoteAppVersion, isNllAppStoreInstalled: $isNllAppStoreInstalled, urlToOpenString: $urlToOpenString")
42 |
43 | try {
44 | val urlToOpen = Uri.parse(urlToOpenString)
45 | try {
46 | /**
47 | * TODO Do we need FLAG_ACTIVITY_NEW_DOCUMENT
48 | * An activity that handles documents can use this attribute so that with every document you open you launch a separate instance of the same activity.
49 | * If you check your recent apps, then you will see various screens of the same activity of your app, each using a different document.
50 | */
51 | val openIntent = Intent(Intent.ACTION_VIEW, urlToOpen).apply {
52 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
53 | }
54 | context.startActivity(openIntent)
55 | } catch (e: Exception) {
56 | CLog.logPrintStackTrace(e)
57 | try {
58 |
59 | } catch (e: Exception) {
60 | Toast.makeText(context, R.string.no_url_handle, Toast.LENGTH_LONG).show()
61 | }
62 |
63 | }
64 | } catch (e: Exception) {
65 | CLog.logPrintStackTrace(e)
66 | Toast.makeText(context, R.string.url_error, Toast.LENGTH_LONG).show()
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/oppoAppMarket/java/com/nll/helper/StoreConfigImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper
2 |
3 | import android.content.ActivityNotFoundException
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.Uri
7 |
8 | object StoreConfigImpl : IStoreConfig {
9 | override fun openACRPhoneDownloadLink(context: Context, packageName: String) = try {
10 | Intent(
11 | Intent.ACTION_VIEW,
12 | Uri.parse("market://details?id=$packageName")
13 | )
14 | .let(context::startActivity)
15 | true
16 |
17 | } catch (ignored: ActivityNotFoundException) {
18 | try {
19 | Intent(
20 | Intent.ACTION_VIEW,
21 | Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
22 | )
23 | .let(context::startActivity)
24 | true
25 | } catch (e: ActivityNotFoundException) {
26 | e.printStackTrace()
27 | false
28 | }
29 | }
30 |
31 | override fun canLinkToWebSite() = false
32 | override fun canLinkToGooglePlayStore() = false
33 | override fun getUpdateCheckUrl() = "https://acr.app/version-oppo-appmarket.json"
34 | override fun requiresProminentPrivacyPolicyDisplay() = true
35 | override fun getPrivacyPolicyUrl()= "https://acr.app/policy.htm"
36 | }
--------------------------------------------------------------------------------
/app/src/oppoAppMarket/java/com/nll/helper/update/DownloadUrlOpenerImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.widget.Toast
7 | import com.nll.helper.R
8 | import com.nll.helper.recorder.CLog
9 | import com.nll.helper.update.contract.IDownloadUrlOpener
10 | import com.nll.helper.update.version.RemoteAppVersion
11 |
12 | object DownloadUrlOpenerImpl : IDownloadUrlOpener {
13 | private const val logTag = "DownloadUrlOpenerImpl"
14 | override fun getOpenDownloadUrlIntent(
15 | context: Context,
16 | remoteAppVersion: RemoteAppVersion
17 | ): Intent {
18 |
19 | CLog.log(logTag, "getOpenDownloadUrlIntent -> remoteAppVersion: $remoteAppVersion")
20 |
21 | return Intent(Intent.ACTION_VIEW, Uri.parse(remoteAppVersion.downloadUrl)).apply {
22 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
23 | }
24 | }
25 |
26 | override fun openDownloadUrl(context: Context, remoteAppVersion: RemoteAppVersion) {
27 |
28 | CLog.log(logTag, "openDownloadUrl -> remoteAppVersion: $remoteAppVersion")
29 |
30 |
31 | try {
32 | val urlToOpen = Uri.parse(remoteAppVersion.downloadUrl)
33 | try {
34 | /**
35 | * TODO Do we need FLAG_ACTIVITY_NEW_DOCUMENT
36 | * An activity that handles documents can use this attribute so that with every document you open you launch a separate instance of the same activity.
37 | * If you check your recent apps, then you will see various screens of the same activity of your app, each using a different document.
38 | */
39 | val openIntent = Intent(Intent.ACTION_VIEW, urlToOpen).apply {
40 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
41 | }
42 | context.startActivity(openIntent)
43 | } catch (e: Exception) {
44 | CLog.logPrintStackTrace(e)
45 | Toast.makeText(context, R.string.no_url_handle, Toast.LENGTH_LONG).show()
46 | }
47 | } catch (e: Exception) {
48 | CLog.logPrintStackTrace(e)
49 | Toast.makeText(context, R.string.url_error, Toast.LENGTH_LONG).show()
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/vivoAppStore/java/com/nll/helper/StoreConfigImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper
2 |
3 | import android.content.ActivityNotFoundException
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.Uri
7 |
8 | object StoreConfigImpl : IStoreConfig {
9 | override fun openACRPhoneDownloadLink(context: Context, packageName: String) = try {
10 | Intent(
11 | Intent.ACTION_VIEW,
12 | Uri.parse("market://details?id=$packageName")
13 | )
14 | .let(context::startActivity)
15 | true
16 |
17 | } catch (ignored: ActivityNotFoundException) {
18 | try {
19 | Intent(
20 | Intent.ACTION_VIEW,
21 | Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
22 | )
23 | .let(context::startActivity)
24 | true
25 | } catch (e: ActivityNotFoundException) {
26 | e.printStackTrace()
27 | false
28 | }
29 | }
30 |
31 | override fun canLinkToWebSite() = true
32 | override fun canLinkToGooglePlayStore() = false
33 | override fun getUpdateCheckUrl() = "https://acr.app/version-vivo-app-store.json"
34 | override fun requiresProminentPrivacyPolicyDisplay() = true
35 | override fun getPrivacyPolicyUrl()= "https://acr.app/policy.htm"
36 | }
--------------------------------------------------------------------------------
/app/src/vivoAppStore/java/com/nll/helper/update/DownloadUrlOpenerImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.widget.Toast
7 | import com.nll.helper.R
8 | import com.nll.helper.recorder.CLog
9 | import com.nll.helper.update.contract.IDownloadUrlOpener
10 | import com.nll.helper.update.version.RemoteAppVersion
11 |
12 | object DownloadUrlOpenerImpl : IDownloadUrlOpener {
13 | private const val logTag = "DownloadUrlOpenerImpl"
14 | override fun getOpenDownloadUrlIntent(
15 | context: Context,
16 | remoteAppVersion: RemoteAppVersion
17 | ): Intent {
18 |
19 | CLog.log(logTag, "getOpenDownloadUrlIntent -> remoteAppVersion: $remoteAppVersion")
20 |
21 | return Intent(Intent.ACTION_VIEW, Uri.parse(remoteAppVersion.downloadUrl)).apply {
22 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
23 | }
24 | }
25 |
26 | override fun openDownloadUrl(context: Context, remoteAppVersion: RemoteAppVersion) {
27 |
28 | CLog.log(logTag, "openDownloadUrl -> remoteAppVersion: $remoteAppVersion")
29 |
30 |
31 | try {
32 | val urlToOpen = Uri.parse(remoteAppVersion.downloadUrl)
33 | try {
34 | /**
35 | * TODO Do we need FLAG_ACTIVITY_NEW_DOCUMENT
36 | * An activity that handles documents can use this attribute so that with every document you open you launch a separate instance of the same activity.
37 | * If you check your recent apps, then you will see various screens of the same activity of your app, each using a different document.
38 | */
39 | val openIntent = Intent(Intent.ACTION_VIEW, urlToOpen).apply {
40 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
41 | }
42 | context.startActivity(openIntent)
43 | } catch (e: Exception) {
44 | CLog.logPrintStackTrace(e)
45 | Toast.makeText(context, R.string.no_url_handle, Toast.LENGTH_LONG).show()
46 | }
47 | } catch (e: Exception) {
48 | CLog.logPrintStackTrace(e)
49 | Toast.makeText(context, R.string.url_error, Toast.LENGTH_LONG).show()
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/xiaomiGetApps/java/com/nll/helper/StoreConfigImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper
2 |
3 | import android.content.ActivityNotFoundException
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.net.Uri
7 |
8 | object StoreConfigImpl : IStoreConfig {
9 | override fun openACRPhoneDownloadLink(context: Context, packageName: String) = try {
10 | Intent(
11 | Intent.ACTION_VIEW,
12 | Uri.parse("market://details?id=$packageName")
13 | )
14 | .let(context::startActivity)
15 | true
16 |
17 | } catch (ignored: ActivityNotFoundException) {
18 | try {
19 | Intent(
20 | Intent.ACTION_VIEW,
21 | Uri.parse("https://play.google.com/store/apps/details?id=$packageName")
22 | )
23 | .let(context::startActivity)
24 | true
25 | } catch (e: ActivityNotFoundException) {
26 | e.printStackTrace()
27 | false
28 | }
29 | }
30 |
31 | override fun canLinkToWebSite() = false
32 | override fun canLinkToGooglePlayStore() = false
33 | override fun getUpdateCheckUrl() = "https://acr.app/version-xiaomi-get-apps.json"
34 | override fun requiresProminentPrivacyPolicyDisplay() = true
35 | override fun getPrivacyPolicyUrl()= "https://acr.app/policy.htm"
36 | }
--------------------------------------------------------------------------------
/app/src/xiaomiGetApps/java/com/nll/helper/update/DownloadUrlOpenerImpl.kt:
--------------------------------------------------------------------------------
1 | package com.nll.helper.update
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import android.widget.Toast
7 | import com.nll.helper.R
8 | import com.nll.helper.recorder.CLog
9 | import com.nll.helper.update.contract.IDownloadUrlOpener
10 | import com.nll.helper.update.version.RemoteAppVersion
11 |
12 | object DownloadUrlOpenerImpl : IDownloadUrlOpener {
13 | private const val logTag = "DownloadUrlOpenerImpl"
14 | override fun getOpenDownloadUrlIntent(
15 | context: Context,
16 | remoteAppVersion: RemoteAppVersion
17 | ): Intent {
18 |
19 | CLog.log(logTag, "getOpenDownloadUrlIntent -> remoteAppVersion: $remoteAppVersion")
20 |
21 | return Intent(Intent.ACTION_VIEW, Uri.parse(remoteAppVersion.downloadUrl)).apply {
22 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
23 | }
24 | }
25 |
26 | override fun openDownloadUrl(context: Context, remoteAppVersion: RemoteAppVersion) {
27 |
28 | CLog.log(logTag, "openDownloadUrl -> remoteAppVersion: $remoteAppVersion")
29 |
30 |
31 | try {
32 | val urlToOpen = Uri.parse(remoteAppVersion.downloadUrl)
33 | try {
34 | /**
35 | * TODO Do we need FLAG_ACTIVITY_NEW_DOCUMENT
36 | * An activity that handles documents can use this attribute so that with every document you open you launch a separate instance of the same activity.
37 | * If you check your recent apps, then you will see various screens of the same activity of your app, each using a different document.
38 | */
39 | val openIntent = Intent(Intent.ACTION_VIEW, urlToOpen).apply {
40 | addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
41 | }
42 | context.startActivity(openIntent)
43 | } catch (e: Exception) {
44 | CLog.logPrintStackTrace(e)
45 | Toast.makeText(context, R.string.no_url_handle, Toast.LENGTH_LONG).show()
46 | }
47 | } catch (e: Exception) {
48 | CLog.logPrintStackTrace(e)
49 | Toast.makeText(context, R.string.url_error, Toast.LENGTH_LONG).show()
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | commonValues = [
4 | appBaseNameSpace : 'com.nll.helper',
5 | releaseMinifyEnabled : true,
6 | debugMinifyEnabled : true,
7 | releaseShrinkResourcesEnabled: false,
8 | debugShrinkResourcesEnabled : false
9 | ]
10 |
11 | }
12 | repositories {
13 | google()
14 | mavenCentral()
15 |
16 | }
17 | dependencies {
18 | classpath "com.android.tools.build:gradle:${libs.versions.androidGradleVersion.get()}"
19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${libs.versions.kotlinVersion.get()}"
20 |
21 | }
22 |
23 |
24 | }
25 |
26 |
27 | plugins {
28 | alias(libs.plugins.android.application) apply(false)
29 | alias(libs.plugins.android.library) apply(false)
30 | alias(libs.plugins.kotlin.android) apply(false)
31 | alias(libs.plugins.banes.versions)
32 | }
33 |
34 | allprojects {
35 | layout.buildDirectory = "${System.properties['user.home']}${File.separator}.build${File.separator}${rootProject.name}${File.separator}${project.name}"
36 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | android.defaults.buildfeatures.viewbinding=true
25 | # Keep this as long as you can. R8 full mode broke many things
26 | android.enableR8.fullMode=false
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NLLAPPS/ACRPhoneHelper/411741baf4d507ddab907d5cf26c17ea7fd7407b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Aug 17 15:38:18 BST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | maven {url= "https://jitpack.io"}
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven {url= "https://jitpack.io"}
15 | }
16 | }
17 | rootProject.name = "ACRPhoneHelper"
18 | include ':app'
--------------------------------------------------------------------------------