Categories
discuss

LiveData onChanged not being called when data changes

I am new to using LiveData and ViewModels and I don’t fully understand how the data is updated.

According to the Android Dev site:

Instead of updating the UI every time the app data changes, your observer can update the UI every time there’s a change.

That sentence doesn’t make sense to me. I’m using a Sqlite database (without Room) and I’m asynchronously getting the data in the getItems() method of the ViewModel. Does that mean when data is added or updated in the database, the LiveData will automatically detect it and onChange is called?

According to the Android dev site, it says to begin observing a LiveData object in onCreate. Before using LiveData, I was refreshing the data in onResume by querying the database then calling notifyDataSetChanged().

This code I have now displays the data when the Fragment loads, but if I delete or add new data to the database, the UI does not reflect the change. I just think I don’t have a basic understanding of how LiveData works and I can’t find a good explanation anywhere.

Help me StackOverflow, you’re my only hope.

public class MyViewModel extends AndroidViewModel {
    private MutableLiveData<List<String>> items;
    private Application application;

    public MyViewModel(@NonNull Application application) {
        super(application);
        this.application = application;
    }

    public MutableLiveData<List<String>> getItems() {
        if (items == null) {
            items = new MutableLiveData<>();
            getItems();
        }
        return items;
    }

    private void getItems() {
        List<String> items = GetItems(); //async process

        this.items.setValue(items)
    }
}

public class ListFragment extends Fragment {

    @Override
    public void onCreate(final Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        MyViewModel model = ViewModelProviders.of(getActivity()).get(MyViewModel.class);

        final Observer<List<String>> observer = new Observer<List<String>>() {
            @Override
            public void onChanged(List<String> items) {
                //update UI
            }
        };

        model.getItems().observe(this, observer);
    }

}

Answer

You’re retrieving items only once if private MutableLiveData<List<String>> items hasn’t been initialised yet:

public MutableLiveData<List<String>> getItems() {
    if (items == null) {
        items = new MutableLiveData<>();
        getItems();// it's called only once 
    }
    return items;
}

One way to overcome this issue could be to use ContentObserver for your data:

...
private final ContentObserver contentObserver = new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        // if there're any changes then perform the request and update the UI
        getItems(); 
    }
};
...

Don’t forget to register it to get notified of changes in the DB:

...
contentResolver.registerContentObserver(<your data content uri>, true, contentObserver);
...

I would also suggest to create a separate Repository layer responsible for data retrieval that could look as follows:

...
private final ContentObserver contentObserver = new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        // if there're any changes then perform the request and update the UI
        getItems(); 
    }
};

public LiveData<List<String>> getItems() {
    return new MutableLiveData<String>() {
        @Override
        public void onActive() {
           // your async operation here
           new Thread({
               List<String> items = db.getItems();
               postValue(items);
           }).start();

           contentResolver.registerContentObserver(<your data content uri>, true, contentObserver); 
        }

        @Override
        public void onInactive() {
            contentResolver.unregisterContentObserver(contentObserver); 
        }
    }
    return liveData;
}   
... 

Then, your ViewModel would need to inject the repository object and hence would require ViewModelFactory to build an instance of your ViewModel but the overall usage would look similar to this:

public class MyViewModel extends AndroidViewModel {   
    ... 
    public MutableLiveData<List<String>> getItems() {
        dataRepository.getItems();
    }
    ...
}

To get more information please read Guide to app architecture

Categories
discuss

How to protect Google Maps API key on Ionic app?

I have Ionic PWA app published for Android and iOS (I used Capacitor to generate the native build). In the frontend code, it has my Google Maps API key, however, I can’t restrict it to any of the options google offers because…

  1. HTTP referrers – It’s not on a public domain name, it’s on a local host within the webview of the native app. http://localhost/ for Android and capacitor://localhost/ for iOS. It does not seem very secure to use these as restrictions as they are very generic, and all other apps will have the same ones.

  2. IP addresses – For obvious reasons.

  3. Android Apps – It’s not within the native code, it’s within a webview.
  4. iOS Apps – It’s not within the native code, it’s within a webview.

    enter image description here

None of these options can work for my situation. So how can I protect my API key from abuse?

Any ideas? I can’t be the only the one using Google Maps API within an Ionic app.

Answer

You can configure the hostname of capacitor apps

"server": {
    // You can configure the local hostname, but it's recommended to keep localhost
    // as it allows to run web APIs that require a secure context such as
    // navigator.geolocation and MediaDevices.getUserMedia.
    "hostname": "unique-app",
  }

and then restrict the the API keys to capacitor://unique-app

https://capacitor.ionicframework.com/docs/basics/configuring-your-app

Categories
discuss

How to open activity (incoming voip call) in Android 10

In Android 10 there apply new restrictions for apps.
We can no longer start an activity from background. While this may be fine for the majority of apps, it’s a killing blow for voip-apps that need to show an incoming call after a push notification arrived.

According to this https://developer.android.com/guide/components/activities/background-starts there is a list of conditions that can be met to still allow opening an activity, but tbh I do not understand that fully (non-english-native here).

What I definitely know, is:

  • I do not have any running activity, task, backstack and the like

  • The app is NOT EVEN RUNNING

What I need to achieve:

  • The FCM service of the app receives a push from our server and shall present the incoming call screen (over lock screen and all – just as it did with android 9 and below)

What can I do to open an activity for an incoming voip call in android 10?
Over the lockscreen and all, just as a normal user would expect from a PHONE app.

Thanks in advance for any hints.

Answer

Use a high-priority notification with a “full-screen intent”. That will:

  • Invoke your “full-screen intent” if the device is locked
  • Otherwise, display a “heads-up” notification
Categories
discuss

Duplicate class from androidx.core:core:1.0.0 and com.android.support:support-compat:26.1.0

This is my build.gradle:

buildscript {
    repositories {
        maven { url 'https://maven.fabric.io/public' }
    }

    dependencies {
        classpath 'io.fabric.tools:gradle:1.+'
    }
}
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

repositories {
    maven { url 'https://maven.fabric.io/public' }
    google()
}


android {
    compileSdkVersion 26
    buildToolsVersion '26.0.2'
    useLibrary 'org.apache.http.legacy'
    defaultConfig {
        multiDexEnabled true
        minSdkVersion 16
        targetSdkVersion 26
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_7
            targetCompatibility JavaVersion.VERSION_1_7
        }
        renderscriptTargetApi 22
        renderscriptSupportModeEnabled true
    }
    buildTypes {
        debug {
            debuggable true
            buildConfigField "boolean", "CRASH_LOGGING", "true"
            applicationIdSuffix ".dev"
        }
        release {
            debuggable false
            buildConfigField "boolean", "CRASH_LOGGING", "true"
            minifyEnabled true
            proguardFiles 'proguard-project.txt'
        }
    }

    flavorDimensions "regular"

    dexOptions {
        javaMaxHeapSize "4g"
    }
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    compile 'com.android.support:multidex:1.0.2'
    compile 'com.android.support:design:26.1.0'
    compile 'com.android.support:recyclerview-v7:26.1.0'
    compile 'com.android.support:appcompat-v7:26.1.0'
    compile 'com.android.support:cardview-v7:26.1.0'
    implementation "com.android.support:support-compat:26.1.0"
    compile 'com.google.android.gms:play-services-auth:16.0.1'
    compile 'com.google.android.gms:play-services-ads:11.8.0'

    compile('com.thoughtworks.xstream:xstream:1.4.7') {
        exclude group: 'xmlpull', module: 'xmlpull'
    }
    compile 'org.apache.commons:commons-lang3:3.6'
    compile 'joda-time:joda-time:2.9.9'
    compile('org.simpleframework:simple-xml:2.7.1') {
        exclude module: 'stax'
        exclude module: 'stax-api'
        exclude module: 'xpp3'
    }
    compile group: 'com.google.code.gson', name: 'gson', version: '2.7'

    // UI & VIEWS
    compile 'com.tuyenmonkey:mkloader:1.4.0'
    compile 'com.mikhaellopez:circularimageview:3.0.2'
    compile 'com.github.mmin18:realtimeblurview:1.1.0'
    compile 'com.github.PhilJay:ValueBar:v1.0.2'

    // IMAGE HANDLING
    compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'

    // NETWORK HANDLING
    compile 'com.android.volley:volley:1.0.0'
    compile 'com.squareup.okhttp3:okhttp:3.9.0'

    //RETORFIT
    compile 'com.squareup.retrofit2:retrofit:2.3.0'
    compile 'com.squareup.retrofit2:converter-gson:2.3.0'
    compile('com.squareup.retrofit2:converter-simplexml:2.3.0') {
        exclude module: 'stax'
        exclude module: 'stax-api'
        exclude module: 'xpp3'
    }
    compile 'com.squareup.retrofit2:converter-scalars:2.3.0'

    // FABRIC
    compile('com.crashlytics.sdk.android:crashlytics:2.7.1@aar') {
        transitive = true;
    }

    // ADS
    compile 'com.flurry.android:analytics:8.2.0@aar'
    compile 'com.appbrain:appbrain-sdk:14.60@aar'
    compile(name: 'android-ad-sdk', ext: 'aar')
    compile(name: 'SOMAAndroid-9.1.5-release', ext: 'aar')
    compile 'com.applovin:applovin-sdk:+'

    compile 'com.android.support.constraint:constraint-layout:1.0.2'

    samsungDebugImplementation files('libs/sdk-v1.0.0.jar')
    samsungDebugImplementation files('libs/motion-v2.2.2.jar')
    samsungReleaseImplementation files('libs/sdk-v1.0.0.jar')
    samsungReleaseImplementation files('libs/motion-v2.2.2.jar')

    //debug DB
    debugCompile 'com.amitshekhar.android:debug-db:1.0.1'

}

apply plugin: 'com.google.gms.google-services'

And when i upgrade this line:

compile 'com.google.android.gms:play-services-ads:18.3.0'

I get this error:

Duplicate class android.support.v4.app.INotificationSideChannel found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.app.INotificationSideChannel$Stub$Proxy found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver$Stub found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.IResultReceiver$Stub$Proxy found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$1 found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$MyResultReceiver found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)
Duplicate class android.support.v4.os.ResultReceiver$MyRunnable found in modules core-1.0.0-runtime.jar (androidx.core:core:1.0.0) and support-compat-26.1.0-runtime.jar (com.android.support:support-compat:26.1.0)

This is the build.gradle file:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    ext.kotlin_version = "1.3.10"
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        classpath 'com.google.gms:google-services:4.3.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
    }
}

allprojects {
    repositories {
        jcenter()
        flatDir {
            dirs 'libs'
        }

        // For MobFox
        maven {
            url "https://jitpack.io"
        }
        // For AppBrain SDK
        maven {
            url 'http://swisscodemonkeys.github.io/appbrain-sdk/maven'
        }
    }
}

Any idea what creates this Duplicate error? How I can fix it?

Answer

You upgrade google play services dependency without checking Document : https://developers.google.com/android/guides/releases, It clearly says for google play services ads 18.0.0 you need androidX in your project. So now update your project to Android X.

Option 1: Either use com.google.android.gms:play-services-ads:17.2.1, which not recommended as you must use latest dependency.

or

Option 2: Go to Android Studio (Use latest Android studio) -> Refactor -> Migrate to AndroidX. Zip/backup your project and run migration.

Categories
discuss

How to resolve “Duplicate class com.google.android.gms.common.api.zzb found in modules jetified-play-services-base-11.0.1-runtime.jar”

I am new in Android Studio Development. I am getting this error in my project.

Duplicate class com.google.android.gms.common.api.zzb found in modules jetified-play-services-base-11.0.1-runtime.jar

Here is my App Gradle dependencies code (build.gradle (Module: app))

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'com.google.android.gms:play-services-ads:18.3.0'
    implementation 'com.google.android.gms:play-services:11.0.1'
}

Here is the full error:

Cause 1: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Duplicate class com.google.android.gms.common.api.zza found in modules jetified-play-services-base-11.0.1-runtime.jar (com.google.android.gms:play-services-base:11.0.1) and jetified-play-services-basement-17.0.0-runtime.jar (com.google.android.gms:play-services-basement:17.0.0)
Duplicate class com.google.android.gms.common.api.zzb found in modules jetified-play-services-base-11.0.1-runtime.jar (com.google.android.gms:play-services-base:11.0.1) and jetified-play-services-basement-17.0.0-runtime.jar (com.google.android.gms:play-services-basement:17.0.0)
Duplicate class com.google.android.gms.common.internal.zzb found in modules jetified-play-services-base-11.0.1-runtime.jar (com.google.android.gms:play-services-base:11.0.1) and jetified-play-services-basement-17.0.0-runtime.jar (com.google.android.gms:play-services-basement:17.0.0)
Duplicate class com.google.android.gms.common.internal.zzq found in modules jetified-play-services-base-11.0.1-runtime.jar (com.google.android.gms:play-services-base:11.0.1) and jetified-play-services-basement-17.0.0-runtime.jar (com.google.android.gms:play-services-basement:17.0.0)
Duplicate class com.google.android.gms.common.internal.zzr found in modules jetified-play-services-base-11.0.1-runtime.jar (com.google.android.gms:play-services-base:11.0.1) and jetified-play-services-basement-17.0.0-runtime.jar (com.google.android.gms:play-services-basement:17.0.0) 
Duplicate class com.google.android.gms.common.internal.zzs found in modules jetified-play-services-base-11.0.1-runtime.jar (com.google.android.gms:play-services-base:11.0.1) and jetified-play-services-basement-17.0.0-runtime.jar (com.google.android.gms:play-services-basement:17.0.0)

I am using FusedLocation Service as well as Google Ads (Admob) in my project.

import com.google.android.gms.common.api.GoogleApi;
import com.google.android.gms.location.FusedLocationProviderClient;
import android.location.Location;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.ads.InterstitialAd;
import com.google.android.gms.ads.MobileAds;

public class MainActivity extends AppCompatActivity
{
    private AdView adView;
    private InterstitialAd interstitialAd;
    FusedLocationProviderClient client;
    ...

Answer

Remove this line:

implementation 'com.google.android.gms:play-services:11.0.1'

play-services-ads:18.3.0 will pull in a rather current version, which doesn’t need to be jetified.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..